Compare commits
404 Commits
v2.2.1
...
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 | ||
|
|
251a1af442 | ||
|
|
9bdf42530a | ||
|
|
8525fbb177 | ||
|
|
63a3568481 | ||
|
|
e4941740de | ||
|
|
25bd33f7da | ||
|
|
2d007b9100 | ||
|
|
b71330c606 | ||
|
|
844b640c8c | ||
|
|
1277e58e68 | ||
|
|
fde6fe324a | ||
|
|
4ed114a4d5 | ||
|
|
8c2dfea6a6 | ||
|
|
a0624df06b | ||
|
|
1eedcf900b | ||
|
|
0b077d6fda | ||
|
|
80047313e7 | ||
|
|
71229b94c8 | ||
|
|
c256daf8c8 | ||
|
|
ba0fb996d2 | ||
|
|
5977e96034 | ||
|
|
a151c5cddd | ||
|
|
0323fd966d | ||
|
|
beb834bc30 | ||
|
|
ad6503c7b3 | ||
|
|
f8c11c8b6b | ||
|
|
ba5421e77b | ||
|
|
20fa803cee | ||
|
|
393fa78a43 | ||
|
|
3f290dae06 | ||
|
|
24d18b9f2f | ||
|
|
79ef8ca371 | ||
|
|
ec12f21113 | ||
|
|
2e8ecc7277 | ||
|
|
fc28b06a0f | ||
|
|
8fdbfdc04c | ||
|
|
bdfcfc96a8 | ||
|
|
bb8649bc81 | ||
|
|
777e3d58b7 | ||
|
|
c552f1aab6 | ||
|
|
c0f2fa3042 | ||
|
|
05936f82bd | ||
|
|
c7db81c97c | ||
|
|
bd1a37b8ef | ||
|
|
efc4e6a8ed | ||
|
|
dd5d87e91e | ||
|
|
ca6df488c5 | ||
|
|
1e018a6aa5 | ||
|
|
0a627f96be | ||
|
|
17a8e67d8a | ||
|
|
815c2c5ad5 | ||
|
|
4376de85ff | ||
|
|
7e89de4612 | ||
|
|
4b72a14706 | ||
|
|
b34f6fedb6 | ||
|
|
58af0d78af | ||
|
|
ca13d9109c | ||
|
|
e103fb5876 | ||
|
|
58178f4563 | ||
|
|
04f1879fd1 | ||
|
|
f5bc9ced0a | ||
|
|
7fe9993f91 | ||
|
|
7c95339324 | ||
|
|
006442f9de | ||
|
|
e20100e437 | ||
|
|
d7cf2b37d5 | ||
|
|
278c2b9aae | ||
|
|
944246fcf5 | ||
|
|
03d87f4993 | ||
|
|
cf8cab5f77 | ||
|
|
49d1376647 | ||
|
|
de5518d262 | ||
|
|
7e4c51f47f | ||
|
|
fe1d153632 | ||
|
|
a98f9ab80e | ||
|
|
867afaf265 | ||
|
|
3d2ec64b14 | ||
|
|
bb407c0b42 | ||
|
|
83c3d901c7 | ||
|
|
901cee903c | ||
|
|
250ea09c7e | ||
|
|
648d59631b | ||
|
|
ed06e3c491 | ||
|
|
3e8d646edd | ||
|
|
9c2c698575 | ||
|
|
e2b0a286a4 | ||
|
|
154809f0f9 | ||
|
|
8d9a51a7c4 | ||
|
|
b3294369d4 | ||
|
|
53730920e3 | ||
|
|
d73b814277 | ||
|
|
dd0050c066 | ||
|
|
ae51ee3e26 | ||
|
|
4b16e5d65f | ||
|
|
4f73bba132 | ||
|
|
3c229602e4 | ||
|
|
c74c902ebc | ||
|
|
8bfd315ba3 | ||
|
|
9d75c47792 | ||
|
|
e183be1a5c | ||
|
|
7e273ce63d | ||
|
|
6d070e75b0 | ||
|
|
f4f96fd18e | ||
|
|
ddd6420d9b | ||
|
|
d76f42296a | ||
|
|
47a6118ffb | ||
|
|
dbd205b73f | ||
|
|
7ef4be26ed | ||
|
|
c6b1979391 | ||
|
|
0f390e65a4 | ||
|
|
5dc0f4e270 | ||
|
|
223288cc52 | ||
|
|
e1f07884b9 | ||
|
|
e00e61edfa | ||
|
|
4f988e186a | ||
|
|
1aa54faa35 | ||
|
|
0bb9247609 | ||
|
|
d841933b21 | ||
|
|
bc8b78a01b | ||
|
|
c6e72be483 | ||
|
|
ef7dd6c8fb | ||
|
|
e6b90385b2 | ||
|
|
61181c6791 | ||
|
|
d2cccd2422 | ||
|
|
b05ebe1598 | ||
|
|
d061f7589c | ||
|
|
15903faf49 | ||
|
|
1908b1a5a6 | ||
|
|
037f472f8c | ||
|
|
a32c1f40b1 | ||
|
|
a00aa27ae4 | ||
|
|
544be77bdc | ||
|
|
b8a110a772 | ||
|
|
7788a2d6bd | ||
|
|
da17fd16fa | ||
|
|
e670f80fed | ||
|
|
2de28b9926 | ||
|
|
d7586af392 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
|
|||||||
liberapay: # Replace with a single Liberapay username
|
liberapay: # Replace with a single Liberapay username
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
otechie: # Replace with a single Otechie username
|
otechie: # Replace with a single Otechie username
|
||||||
custom: ['https://mempool.space/about'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
custom: ['https://mempool.space/sponsor'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
|
|||||||
52
.github/workflows/cypress.yml
vendored
52
.github/workflows/cypress.yml
vendored
@@ -15,17 +15,63 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: ${{ matrix.browser }} browser tests
|
- 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)
|
||||||
uses: cypress-io/github-action@v2
|
uses: cypress-io/github-action@v2
|
||||||
with:
|
with:
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
build: npm run config:defaults
|
build: npm run config:defaults:mempool
|
||||||
start: npm run start:local-prod
|
start: npm run start:local-prod
|
||||||
wait-on: 'http://localhost:4200'
|
wait-on: 'http://localhost:4200'
|
||||||
wait-on-timeout: 120
|
wait-on-timeout: 120
|
||||||
record: true
|
record: true
|
||||||
parallel: true
|
parallel: true
|
||||||
group: Tests on ${{ matrix.browser }}
|
group: Tests on ${{ matrix.browser }} (Mempool)
|
||||||
|
browser: ${{ matrix.browser }}
|
||||||
|
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||||
|
env:
|
||||||
|
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
|
||||||
|
|
||||||
|
- name: ${{ matrix.browser }} browser tests (Liquid)
|
||||||
|
uses: cypress-io/github-action@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
working-directory: frontend
|
||||||
|
build: npm run config:defaults:liquid
|
||||||
|
start: npm run start:local-prod
|
||||||
|
wait-on: 'http://localhost:4200'
|
||||||
|
wait-on-timeout: 120
|
||||||
|
record: true
|
||||||
|
parallel: true
|
||||||
|
spec: cypress/integration/liquid/liquid.spec.ts
|
||||||
|
group: Tests on ${{ matrix.browser }} (Liquid)
|
||||||
|
browser: ${{ matrix.browser }}
|
||||||
|
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||||
|
env:
|
||||||
|
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
|
||||||
|
|
||||||
|
- name: ${{ matrix.browser }} browser tests (Bisq)
|
||||||
|
uses: cypress-io/github-action@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
working-directory: frontend
|
||||||
|
build: npm run config:defaults:bisq
|
||||||
|
start: npm run start:local-prod
|
||||||
|
wait-on: 'http://localhost:4200'
|
||||||
|
wait-on-timeout: 120
|
||||||
|
record: true
|
||||||
|
parallel: true
|
||||||
|
spec: cypress/integration/bisq/bisq.spec.ts
|
||||||
|
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 }}'
|
||||||
env:
|
env:
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"typescript.tsdk": "./backend/node_modules/typescript/lib"
|
||||||
|
}
|
||||||
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/
|
||||||
|
|||||||
4
backend/.vscode/settings.json
vendored
Normal file
4
backend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"typescript.tsdk": "../backend/node_modules/typescript/lib"
|
||||||
|
}
|
||||||
@@ -8,7 +8,12 @@
|
|||||||
"POLL_RATE_MS": 2000,
|
"POLL_RATE_MS": 2000,
|
||||||
"CACHE_DIR": "./cache",
|
"CACHE_DIR": "./cache",
|
||||||
"CLEAR_PROTECTION_MINUTES": 20,
|
"CLEAR_PROTECTION_MINUTES": 20,
|
||||||
"RECOMMENDED_FEE_PERCENTILE": 50
|
"RECOMMENDED_FEE_PERCENTILE": 50,
|
||||||
|
"BLOCK_WEIGHT_UNITS": 4000000,
|
||||||
|
"INITIAL_BLOCKS_AMOUNT": 8,
|
||||||
|
"MEMPOOL_BLOCKS_AMOUNT": 8,
|
||||||
|
"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",
|
||||||
@@ -24,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.1",
|
"version": "2.3.0-dev",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.1",
|
"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.1.5"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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,10 +1306,9 @@
|
|||||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.2.3",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||||
"integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
|
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -1537,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",
|
||||||
@@ -1665,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",
|
||||||
@@ -1691,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": "*"
|
||||||
}
|
}
|
||||||
@@ -1732,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": {
|
||||||
@@ -1746,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",
|
||||||
@@ -1859,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",
|
||||||
@@ -1982,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",
|
||||||
@@ -2009,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",
|
||||||
@@ -2034,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",
|
||||||
@@ -2119,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",
|
||||||
@@ -2139,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",
|
||||||
@@ -2212,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",
|
||||||
@@ -2346,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",
|
||||||
@@ -2374,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",
|
||||||
@@ -2414,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",
|
||||||
@@ -2429,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"
|
||||||
}
|
}
|
||||||
@@ -2462,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",
|
||||||
@@ -2506,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": {
|
||||||
@@ -2530,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",
|
||||||
@@ -2702,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",
|
||||||
@@ -2770,10 +2441,9 @@
|
|||||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.2.3",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||||
"integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
|
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -2818,9 +2488,10 @@
|
|||||||
"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": {}
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.1",
|
"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.1.5"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class BackendInfo {
|
|||||||
try {
|
try {
|
||||||
this.gitCommitHash = fs.readFileSync('../.git/refs/heads/master').toString().trim();
|
this.gitCommitHash = fs.readFileSync('../.git/refs/heads/master').toString().trim();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Could not load git commit info: ' + e.message || e);
|
logger.err('Could not load git commit info: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ class BackendInfo {
|
|||||||
const packageJson = fs.readFileSync('package.json').toString();
|
const packageJson = fs.readFileSync('package.json').toString();
|
||||||
this.version = JSON.parse(packageJson).version;
|
this.version = JSON.parse(packageJson).version;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(e);
|
throw new Error(e instanceof Error ? e.message : 'Error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class Bisq {
|
|||||||
this.buildIndex();
|
this.buildIndex();
|
||||||
this.calculateStats();
|
this.calculateStats();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.info('loadBisqDumpFile() error.' + e.message || e);
|
logger.info('loadBisqDumpFile() error.' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class Bisq {
|
|||||||
logger.debug('Bisq market data updated in ' + time + ' ms');
|
logger.debug('Bisq market data updated in ' + time + ' ms');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('loadBisqMarketDataDumpFile() error.' + e.message || e);
|
logger.err('loadBisqMarketDataDumpFile() error.' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,28 +82,36 @@ 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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddressInformation {
|
export interface AddressInformation {
|
||||||
isvalid: boolean; // (boolean) If the address is valid or not. If not, this is the only property returned.
|
isvalid: boolean; // (boolean) If the address is valid or not. If not, this is the only property returned.
|
||||||
|
isvalid_parent?: boolean; // (boolean) Elements only
|
||||||
address: string; // (string) The bitcoin address validated
|
address: string; // (string) The bitcoin address validated
|
||||||
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
|
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
|
||||||
isscript: boolean; // (boolean) If the key is a script
|
isscript: boolean; // (boolean) If the key is a script
|
||||||
iswitness: boolean; // (boolean) If the address is a witness
|
iswitness: boolean; // (boolean) If the address is a witness
|
||||||
witness_version?: boolean; // (numeric, optional) The version number of the witness program
|
witness_version?: boolean; // (numeric, optional) The version number of the witness program
|
||||||
witness_program: string; // (string, optional) The hex value of the witness program
|
witness_program: string; // (string, optional) The hex value of the witness program
|
||||||
|
confidential_key?: string; // (string) Elements only
|
||||||
|
unconfidential?: string; // (string) Elements only
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChainTips {
|
export interface ChainTips {
|
||||||
|
|||||||
@@ -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> {
|
||||||
@@ -60,13 +52,12 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
return this.bitcoindClient.getBlock(hash, 0);
|
return this.bitcoindClient.getBlock(hash, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$getBlockHash(height: number): Promise<string> {
|
$getBlockHash(height: number): Promise<string> {
|
||||||
return this.bitcoindClient.getBlockHash(height);
|
return this.bitcoindClient.getBlockHash(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
$getBlockHeader(hash: string): Promise<string> {
|
$getBlockHeader(hash: string): Promise<string> {
|
||||||
return this.bitcoindClient.getBlockHeader(hash,false);
|
return this.bitcoindClient.getBlockHeader(hash, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async $getBlock(hash: string): Promise<IEsploraApi.Block> {
|
async $getBlock(hash: string): Promise<IEsploraApi.Block> {
|
||||||
@@ -107,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,
|
||||||
@@ -124,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),
|
||||||
};
|
};
|
||||||
@@ -238,10 +234,6 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $validateAddress(address: string): Promise<IBitcoinApi.AddressInformation> {
|
|
||||||
return this.bitcoindClient.validateAddress(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private $getMempoolEntry(txid: string): Promise<IBitcoinApi.MempoolEntry> {
|
private $getMempoolEntry(txid: string): Promise<IBitcoinApi.MempoolEntry> {
|
||||||
return this.bitcoindClient.getMempoolEntry(txid);
|
return this.bitcoindClient.getMempoolEntry(txid);
|
||||||
}
|
}
|
||||||
@@ -307,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,49 +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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,10 +1,8 @@
|
|||||||
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';
|
||||||
@@ -15,8 +13,8 @@ import memoryCache from '../memory-cache';
|
|||||||
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 };
|
||||||
@@ -44,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 this.$validateAddress(address);
|
const addressInfo = await this.bitcoindClient.validateAddress(address);
|
||||||
if (!addressInfo || !addressInfo.isvalid) {
|
if (!addressInfo || !addressInfo.isvalid) {
|
||||||
return ({
|
return ({
|
||||||
'address': address,
|
'address': address,
|
||||||
@@ -89,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(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
|
async $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
|
||||||
const addressInfo = await this.$validateAddress(address);
|
const addressInfo = await this.bitcoindClient.validateAddress(address);
|
||||||
if (!addressInfo || !addressInfo.isvalid) {
|
if (!addressInfo || !addressInfo.isvalid) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -126,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(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,10 +6,9 @@ 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 static INITIAL_BLOCK_AMOUNT = 8;
|
|
||||||
private blocks: BlockExtended[] = [];
|
private blocks: BlockExtended[] = [];
|
||||||
private currentBlockHeight = 0;
|
private currentBlockHeight = 0;
|
||||||
private currentDifficulty = 0;
|
private currentDifficulty = 0;
|
||||||
@@ -35,18 +34,18 @@ class Blocks {
|
|||||||
const blockHeightTip = await bitcoinApi.$getBlockHeightTip();
|
const blockHeightTip = await bitcoinApi.$getBlockHeightTip();
|
||||||
|
|
||||||
if (this.blocks.length === 0) {
|
if (this.blocks.length === 0) {
|
||||||
this.currentBlockHeight = blockHeightTip - Blocks.INITIAL_BLOCK_AMOUNT;
|
this.currentBlockHeight = blockHeightTip - config.MEMPOOL.INITIAL_BLOCKS_AMOUNT;
|
||||||
} else {
|
} else {
|
||||||
this.currentBlockHeight = this.blocks[this.blocks.length - 1].height;
|
this.currentBlockHeight = this.blocks[this.blocks.length - 1].height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blockHeightTip - this.currentBlockHeight > Blocks.INITIAL_BLOCK_AMOUNT * 2) {
|
if (blockHeightTip - this.currentBlockHeight > config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 2) {
|
||||||
logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${Blocks.INITIAL_BLOCK_AMOUNT} recent blocks`);
|
logger.info(`${blockHeightTip - this.currentBlockHeight} blocks since tip. Fast forwarding to the ${config.MEMPOOL.INITIAL_BLOCKS_AMOUNT} recent blocks`);
|
||||||
this.currentBlockHeight = blockHeightTip - Blocks.INITIAL_BLOCK_AMOUNT;
|
this.currentBlockHeight = blockHeightTip - config.MEMPOOL.INITIAL_BLOCKS_AMOUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -90,7 +89,7 @@ class Blocks {
|
|||||||
const tx = await transactionUtils.$getTransactionExtended(txIds[i]);
|
const tx = await transactionUtils.$getTransactionExtended(txIds[i]);
|
||||||
transactions.push(tx);
|
transactions.push(tx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error fetching block tx: ' + e.message || e);
|
logger.debug('Error fetching block tx: ' + (e instanceof Error ? e.message : e));
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
throw new Error('Failed to fetch Coinbase transaction: ' + txIds[i]);
|
throw new Error('Failed to fetch Coinbase transaction: ' + txIds[i]);
|
||||||
}
|
}
|
||||||
@@ -111,8 +110,8 @@ class Blocks {
|
|||||||
blockExtended.coinbaseTx = transactionUtils.stripCoinbaseTransaction(transactions[0]);
|
blockExtended.coinbaseTx = transactionUtils.stripCoinbaseTransaction(transactions[0]);
|
||||||
transactions.shift();
|
transactions.shift();
|
||||||
transactions.sort((a, b) => b.effectiveFeePerVsize - a.effectiveFeePerVsize);
|
transactions.sort((a, b) => b.effectiveFeePerVsize - a.effectiveFeePerVsize);
|
||||||
blockExtended.medianFee = transactions.length > 1 ? Common.median(transactions.map((tx) => tx.effectiveFeePerVsize)) : 0;
|
blockExtended.medianFee = transactions.length > 0 ? Common.median(transactions.map((tx) => tx.effectiveFeePerVsize)) : 0;
|
||||||
blockExtended.feeRange = transactions.length > 1 ? Common.getFeesInRange(transactions, 8) : [0, 0];
|
blockExtended.feeRange = transactions.length > 0 ? Common.getFeesInRange(transactions, 8) : [0, 0];
|
||||||
|
|
||||||
if (block.height % 2016 === 0) {
|
if (block.height % 2016 === 0) {
|
||||||
this.previousDifficultyRetarget = (block.difficulty - this.currentDifficulty) / this.currentDifficulty * 100;
|
this.previousDifficultyRetarget = (block.difficulty - this.currentDifficulty) / this.currentDifficulty * 100;
|
||||||
@@ -121,8 +120,8 @@ class Blocks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.blocks.push(blockExtended);
|
this.blocks.push(blockExtended);
|
||||||
if (this.blocks.length > Blocks.INITIAL_BLOCK_AMOUNT * 4) {
|
if (this.blocks.length > config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 4) {
|
||||||
this.blocks = this.blocks.slice(-Blocks.INITIAL_BLOCK_AMOUNT * 4);
|
this.blocks = this.blocks.slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.newBlockCallbacks.length) {
|
if (this.newBlockCallbacks.length) {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { CpfpInfo, TransactionExtended, TransactionStripped } from '../mempool.interfaces';
|
import { CpfpInfo, TransactionExtended, TransactionStripped } from '../mempool.interfaces';
|
||||||
|
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;
|
||||||
@@ -105,7 +107,7 @@ export class Common {
|
|||||||
totalFees += tx.bestDescendant.fee;
|
totalFees += tx.bestDescendant.fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.effectiveFeePerVsize = Math.max(1, totalFees / (totalWeight / 4));
|
tx.effectiveFeePerVsize = Math.max(config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1, totalFees / (totalWeight / 4));
|
||||||
tx.cpfpChecked = true;
|
tx.cpfpChecked = true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class DiskCache {
|
|||||||
logger.debug('Mempool and blocks data saved to disk cache');
|
logger.debug('Mempool and blocks data saved to disk cache');
|
||||||
this.isWritingCache = false;
|
this.isWritingCache = false;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn('Error writing to cache file: ' + e.message || e);
|
logger.warn('Error writing to cache file: ' + (e instanceof Error ? e.message : e));
|
||||||
this.isWritingCache = false;
|
this.isWritingCache = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { IConversionRates } from '../mempool.interfaces';
|
import { IConversionRates } from '../mempool.interfaces';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
class FiatConversion {
|
class FiatConversion {
|
||||||
private conversionRates: IConversionRates = {
|
private conversionRates: IConversionRates = {
|
||||||
@@ -16,7 +17,7 @@ class FiatConversion {
|
|||||||
|
|
||||||
public startService() {
|
public startService() {
|
||||||
logger.info('Starting currency rates service');
|
logger.info('Starting currency rates service');
|
||||||
setInterval(this.updateCurrency.bind(this), 1000 * 60);
|
setInterval(this.updateCurrency.bind(this), 1000 * config.MEMPOOL.PRICE_FEED_UPDATE_INTERVAL);
|
||||||
this.updateCurrency();
|
this.updateCurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ class FiatConversion {
|
|||||||
this.ratesChangedCallback(this.conversionRates);
|
this.ratesChangedCallback(this.conversionRates);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Error updating fiat conversion rates: ' + e);
|
logger.err('Error updating fiat conversion rates: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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();
|
||||||
@@ -4,7 +4,6 @@ import { Common } from './common';
|
|||||||
import config from '../config';
|
import config from '../config';
|
||||||
|
|
||||||
class MempoolBlocks {
|
class MempoolBlocks {
|
||||||
private static DEFAULT_PROJECTED_BLOCKS_AMOUNT = 8;
|
|
||||||
private mempoolBlocks: MempoolBlockWithTransactions[] = [];
|
private mempoolBlocks: MempoolBlockWithTransactions[] = [];
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@@ -72,29 +71,30 @@ class MempoolBlocks {
|
|||||||
|
|
||||||
private calculateMempoolBlocks(transactionsSorted: TransactionExtended[]): MempoolBlockWithTransactions[] {
|
private calculateMempoolBlocks(transactionsSorted: TransactionExtended[]): MempoolBlockWithTransactions[] {
|
||||||
const mempoolBlocks: MempoolBlockWithTransactions[] = [];
|
const mempoolBlocks: MempoolBlockWithTransactions[] = [];
|
||||||
let blockVSize = 0;
|
let blockWeight = 0;
|
||||||
let blockSize = 0;
|
let blockSize = 0;
|
||||||
let transactions: TransactionExtended[] = [];
|
let transactions: TransactionExtended[] = [];
|
||||||
transactionsSorted.forEach((tx) => {
|
transactionsSorted.forEach((tx) => {
|
||||||
if (blockVSize + tx.vsize <= 1000000 || mempoolBlocks.length === MempoolBlocks.DEFAULT_PROJECTED_BLOCKS_AMOUNT - 1) {
|
if (blockWeight + tx.weight <= config.MEMPOOL.BLOCK_WEIGHT_UNITS
|
||||||
blockVSize += tx.vsize;
|
|| mempoolBlocks.length === config.MEMPOOL.MEMPOOL_BLOCKS_AMOUNT - 1) {
|
||||||
|
blockWeight += tx.weight;
|
||||||
blockSize += tx.size;
|
blockSize += tx.size;
|
||||||
transactions.push(tx);
|
transactions.push(tx);
|
||||||
} else {
|
} else {
|
||||||
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockVSize, mempoolBlocks.length));
|
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockWeight, mempoolBlocks.length));
|
||||||
blockVSize = tx.vsize;
|
blockWeight = tx.weight;
|
||||||
blockSize = tx.size;
|
blockSize = tx.size;
|
||||||
transactions = [tx];
|
transactions = [tx];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (transactions.length) {
|
if (transactions.length) {
|
||||||
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockVSize, mempoolBlocks.length));
|
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockWeight, mempoolBlocks.length));
|
||||||
}
|
}
|
||||||
return mempoolBlocks;
|
return mempoolBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private dataToMempoolBlocks(transactions: TransactionExtended[],
|
private dataToMempoolBlocks(transactions: TransactionExtended[],
|
||||||
blockSize: number, blockVSize: number, blocksIndex: number): MempoolBlockWithTransactions {
|
blockSize: number, blockWeight: number, blocksIndex: number): MempoolBlockWithTransactions {
|
||||||
let rangeLength = 4;
|
let rangeLength = 4;
|
||||||
if (blocksIndex === 0) {
|
if (blocksIndex === 0) {
|
||||||
rangeLength = 8;
|
rangeLength = 8;
|
||||||
@@ -106,7 +106,7 @@ class MempoolBlocks {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
blockSize: blockSize,
|
blockSize: blockSize,
|
||||||
blockVSize: blockVSize,
|
blockVSize: blockWeight / 4,
|
||||||
nTx: transactions.length,
|
nTx: transactions.length,
|
||||||
totalFees: transactions.reduce((acc, cur) => acc + cur.fee, 0),
|
totalFees: transactions.reduce((acc, cur) => acc + cur.fee, 0),
|
||||||
medianFee: Common.percentile(transactions.map((tx) => tx.effectiveFeePerVsize), config.MEMPOOL.RECOMMENDED_FEE_PERCENTILE),
|
medianFee: Common.percentile(transactions.map((tx) => tx.effectiveFeePerVsize), config.MEMPOOL.RECOMMENDED_FEE_PERCENTILE),
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -124,7 +125,7 @@ class Mempool {
|
|||||||
}
|
}
|
||||||
newTransactions.push(transaction);
|
newTransactions.push(transaction);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 {
|
||||||
@@ -255,7 +263,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return result.insertId;
|
return result.insertId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$create() error' + e.message || e);
|
logger.err('$create() error' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +321,7 @@ class Statistics {
|
|||||||
return this.mapStatisticToOptimizedStatistic([rows[0]])[0];
|
return this.mapStatisticToOptimizedStatistic([rows[0]])[0];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list2H() error' + e.message || e);
|
logger.err('$list2H() error' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +333,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list2H() error' + e.message || e);
|
logger.err('$list2H() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +346,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list24h() error' + e.message || e);
|
logger.err('$list24h() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,7 +359,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list1W() error' + e);
|
logger.err('$list1W() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +372,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list1M() error' + e);
|
logger.err('$list1M() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,7 +385,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list3M() error' + e);
|
logger.err('$list3M() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,7 +398,7 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list6M() error' + e);
|
logger.err('$list6M() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
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 {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
||||||
import logger from '../logger';
|
|
||||||
import { TransactionExtended, TransactionMinerInfo } from '../mempool.interfaces';
|
import { TransactionExtended, TransactionMinerInfo } from '../mempool.interfaces';
|
||||||
import { IEsploraApi } from './bitcoin/esplora-api.interface';
|
import { IEsploraApi } from './bitcoin/esplora-api.interface';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
class TransactionUtils {
|
class TransactionUtils {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
@@ -31,7 +31,7 @@ class TransactionUtils {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
const feePerVbytes = Math.max(1, (transaction.fee || 0) / (transaction.weight / 4));
|
const feePerVbytes = Math.max(config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1, (transaction.fee || 0) / (transaction.weight / 4));
|
||||||
const transactionExtended: TransactionExtended = Object.assign({
|
const transactionExtended: TransactionExtended = Object.assign({
|
||||||
vsize: Math.round(transaction.weight / 4),
|
vsize: Math.round(transaction.weight / 4),
|
||||||
feePerVsize: feePerVbytes,
|
feePerVsize: feePerVbytes,
|
||||||
|
|||||||
@@ -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() { }
|
||||||
@@ -61,7 +60,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
response['tx'] = fullTx;
|
response['tx'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction: ' + e.message || e);
|
logger.debug('Error finding transaction: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -69,7 +68,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(client['track-tx'], true);
|
const fullTx = await transactionUtils.$getTransactionExtended(client['track-tx'], true);
|
||||||
response['tx'] = fullTx;
|
response['tx'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction. ' + e.message || e);
|
logger.debug('Error finding transaction. ' + (e instanceof Error ? e.message : e));
|
||||||
client['track-mempool-tx'] = parsedMessage['track-tx'];
|
client['track-mempool-tx'] = parsedMessage['track-tx'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,9 +79,13 @@ class WebsocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parsedMessage && parsedMessage['track-address']) {
|
if (parsedMessage && parsedMessage['track-address']) {
|
||||||
if (/^([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,87})$/
|
if (/^([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})$/
|
||||||
.test(parsedMessage['track-address'])) {
|
.test(parsedMessage['track-address'])) {
|
||||||
client['track-address'] = parsedMessage['track-address'];
|
let matchedAddress = parsedMessage['track-address'];
|
||||||
|
if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(parsedMessage['track-address'])) {
|
||||||
|
matchedAddress = matchedAddress.toLowerCase();
|
||||||
|
}
|
||||||
|
client['track-address'] = matchedAddress;
|
||||||
} else {
|
} else {
|
||||||
client['track-address'] = null;
|
client['track-address'] = null;
|
||||||
}
|
}
|
||||||
@@ -97,7 +100,7 @@ class WebsocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parsedMessage.action === 'init') {
|
if (parsedMessage.action === 'init') {
|
||||||
const _blocks = blocks.getBlocks().slice(-8);
|
const _blocks = blocks.getBlocks().slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT);
|
||||||
if (!_blocks) {
|
if (!_blocks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -124,7 +127,7 @@ class WebsocketHandler {
|
|||||||
client.send(JSON.stringify(response));
|
client.send(JSON.stringify(response));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error parsing websocket message: ' + e.message || e);
|
logger.debug('Error parsing websocket message: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -173,7 +176,7 @@ class WebsocketHandler {
|
|||||||
|
|
||||||
getInitData(_blocks?: BlockExtended[]) {
|
getInitData(_blocks?: BlockExtended[]) {
|
||||||
if (!_blocks) {
|
if (!_blocks) {
|
||||||
_blocks = blocks.getBlocks().slice(-8);
|
_blocks = blocks.getBlocks().slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
'mempoolInfo': memPool.getMempoolInfo(),
|
'mempoolInfo': memPool.getMempoolInfo(),
|
||||||
@@ -252,7 +255,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
response['tx'] = fullTx;
|
response['tx'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response['tx'] = tx;
|
response['tx'] = tx;
|
||||||
@@ -272,7 +275,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
foundTransactions.push(fullTx);
|
foundTransactions.push(fullTx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundTransactions.push(tx);
|
foundTransactions.push(tx);
|
||||||
@@ -286,7 +289,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(tx.txid, true);
|
||||||
foundTransactions.push(fullTx);
|
foundTransactions.push(fullTx);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundTransactions.push(tx);
|
foundTransactions.push(tx);
|
||||||
@@ -304,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;
|
||||||
@@ -337,7 +340,7 @@ class WebsocketHandler {
|
|||||||
const fullTx = await transactionUtils.$getTransactionExtended(rbfTransaction, true);
|
const fullTx = await transactionUtils.$getTransactionExtended(rbfTransaction, true);
|
||||||
response['rbfTransaction'] = fullTx;
|
response['rbfTransaction'] = fullTx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('Error finding transaction in mempool: ' + e.message || e);
|
logger.debug('Error finding transaction in mempool: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response['rbfTransaction'] = rbfTx;
|
response['rbfTransaction'] = rbfTx;
|
||||||
@@ -435,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;
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ interface IConfig {
|
|||||||
CACHE_DIR: string;
|
CACHE_DIR: string;
|
||||||
CLEAR_PROTECTION_MINUTES: number;
|
CLEAR_PROTECTION_MINUTES: number;
|
||||||
RECOMMENDED_FEE_PERCENTILE: number;
|
RECOMMENDED_FEE_PERCENTILE: number;
|
||||||
|
BLOCK_WEIGHT_UNITS: number;
|
||||||
|
INITIAL_BLOCKS_AMOUNT: number;
|
||||||
|
MEMPOOL_BLOCKS_AMOUNT: number;
|
||||||
|
PRICE_FEED_UPDATE_INTERVAL: number;
|
||||||
|
USE_SECOND_NODE_FOR_MINFEE: boolean;
|
||||||
};
|
};
|
||||||
ESPLORA: {
|
ESPLORA: {
|
||||||
REST_API_URL: string;
|
REST_API_URL: string;
|
||||||
@@ -26,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;
|
||||||
@@ -69,6 +73,11 @@ const defaults: IConfig = {
|
|||||||
'CACHE_DIR': './cache',
|
'CACHE_DIR': './cache',
|
||||||
'CLEAR_PROTECTION_MINUTES': 20,
|
'CLEAR_PROTECTION_MINUTES': 20,
|
||||||
'RECOMMENDED_FEE_PERCENTILE': 50,
|
'RECOMMENDED_FEE_PERCENTILE': 50,
|
||||||
|
'BLOCK_WEIGHT_UNITS': 4000000,
|
||||||
|
'INITIAL_BLOCKS_AMOUNT': 8,
|
||||||
|
'MEMPOOL_BLOCKS_AMOUNT': 8,
|
||||||
|
'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',
|
||||||
@@ -84,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',
|
||||||
@@ -121,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'];
|
||||||
@@ -133,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,7 +20,7 @@ export async function checkDbConnection() {
|
|||||||
logger.info('Database connection established.');
|
logger.info('Database connection established.');
|
||||||
connection.release();
|
connection.release();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Could not connect to database: ' + e.message || e);
|
logger.err('Could not connect to database: ' + (e instanceof Error ? e.message : e));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 });
|
||||||
@@ -111,8 +113,8 @@ class Server {
|
|||||||
try {
|
try {
|
||||||
await memPool.$updateMemPoolInfo();
|
await memPool.$updateMemPoolInfo();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = `updateMempoolInfo: ${(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);
|
||||||
@@ -123,7 +125,7 @@ class Server {
|
|||||||
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
setTimeout(this.runMainUpdateLoop.bind(this), config.MEMPOOL.POLL_RATE_MS);
|
||||||
this.currentBackendRetryInterval = 5;
|
this.currentBackendRetryInterval = 5;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const loggerMsg = `runMainLoop error: ${(e.message || e)}. Retrying in ${this.currentBackendRetryInterval} sec.`;
|
const loggerMsg = `runMainLoop error: ${(e instanceof Error ? e.message : e)}. Retrying in ${this.currentBackendRetryInterval} sec.`;
|
||||||
if (this.currentBackendRetryInterval > 5) {
|
if (this.currentBackendRetryInterval > 5) {
|
||||||
logger.warn(loggerMsg);
|
logger.warn(loggerMsg);
|
||||||
mempool.setOutOfSync();
|
mempool.setOutOfSync();
|
||||||
@@ -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));
|
||||||
@@ -158,6 +169,8 @@ class Server {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'fees/mempool-blocks', routes.getMempoolBlocks)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'fees/mempool-blocks', routes.getMempoolBlocks)
|
||||||
.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)
|
||||||
|
.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 });
|
||||||
@@ -205,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))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,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)
|
||||||
@@ -253,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,6 +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 bitcoinClient from './api/bitcoin/bitcoin-client';
|
||||||
|
import elementsParser from './api/liquid/elements-parser';
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@@ -50,12 +52,20 @@ 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();
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +84,7 @@ class Routes {
|
|||||||
const result = mempoolBlocks.getMempoolBlocks();
|
const result = mempoolBlocks.getMempoolBlocks();
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,10 +487,10 @@ class Routes {
|
|||||||
res.json(transaction);
|
res.json(transaction);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
}
|
}
|
||||||
res.status(statusCode).send(e.message || e);
|
res.status(statusCode).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,10 +501,10 @@ class Routes {
|
|||||||
res.send(transaction.hex);
|
res.send(transaction.hex);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
}
|
}
|
||||||
res.status(statusCode).send(e.message || e);
|
res.status(statusCode).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,10 +514,10 @@ class Routes {
|
|||||||
res.json(transaction.status);
|
res.json(transaction.status);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
}
|
}
|
||||||
res.status(statusCode).send(e.message || e);
|
res.status(statusCode).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +526,7 @@ class Routes {
|
|||||||
const result = await bitcoinApi.$getBlock(req.params.hash);
|
const result = await bitcoinApi.$getBlock(req.params.hash);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +536,7 @@ class Routes {
|
|||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
res.send(blockHeader);
|
res.send(blockHeader);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,7 +573,7 @@ class Routes {
|
|||||||
res.json(returnBlocks);
|
res.json(returnBlocks);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadingIndicators.setProgress('blocks', 100);
|
loadingIndicators.setProgress('blocks', 100);
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,13 +592,13 @@ class Routes {
|
|||||||
transactions.push(transaction);
|
transactions.push(transaction);
|
||||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100);
|
loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.debug('getBlockTransactions error: ' + e.message || e);
|
logger.debug('getBlockTransactions error: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100);
|
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100);
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,7 +607,7 @@ class Routes {
|
|||||||
const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10));
|
const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10));
|
||||||
res.send(blockHash);
|
res.send(blockHash);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,10 +621,10 @@ 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.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.message);
|
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,10 +638,10 @@ 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.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.message);
|
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,7 +654,7 @@ class Routes {
|
|||||||
const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
||||||
res.send(blockHash);
|
res.send(blockHash);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,7 +675,7 @@ class Routes {
|
|||||||
const rawMempool = await bitcoinApi.$getRawMempool();
|
const rawMempool = await bitcoinApi.$getRawMempool();
|
||||||
res.send(rawMempool);
|
res.send(rawMempool);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,7 +684,7 @@ class Routes {
|
|||||||
const result = await bitcoinApi.$getBlockHeightTip();
|
const result = await bitcoinApi.$getBlockHeightTip();
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,7 +693,16 @@ class Routes {
|
|||||||
const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash);
|
const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e.message || e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async validateAddress(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const result = await bitcoinClient.validateAddress(req.params.address);
|
||||||
|
res.json(result);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,42 +712,99 @@ class Routes {
|
|||||||
|
|
||||||
public getDifficultyChange(req: Request, res: Response) {
|
public getDifficultyChange(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const now = new Date().getTime() / 1000;
|
|
||||||
const DATime = blocks.getLastDifficultyAdjustmentTime();
|
const DATime = blocks.getLastDifficultyAdjustmentTime();
|
||||||
const previousRetarget = blocks.getPreviousDifficultyRetarget();
|
const previousRetarget = blocks.getPreviousDifficultyRetarget();
|
||||||
const diff = now - DATime;
|
|
||||||
const blockHeight = blocks.getCurrentBlockHeight();
|
const blockHeight = blocks.getCurrentBlockHeight();
|
||||||
|
|
||||||
|
const now = new Date().getTime() / 1000;
|
||||||
|
const diff = now - DATime;
|
||||||
const blocksInEpoch = blockHeight % 2016;
|
const blocksInEpoch = blockHeight % 2016;
|
||||||
const difficultyChange = (600 / (diff / blocksInEpoch) - 1) * 100;
|
const progressPercent = (blocksInEpoch >= 0) ? blocksInEpoch / 2016 * 100 : 100;
|
||||||
|
const remainingBlocks = 2016 - blocksInEpoch;
|
||||||
|
const nextRetargetHeight = blockHeight + remainingBlocks;
|
||||||
|
|
||||||
|
let difficultyChange = 0;
|
||||||
|
if (remainingBlocks < 1870) {
|
||||||
|
if (blocksInEpoch > 0) {
|
||||||
|
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||||
|
}
|
||||||
|
if (difficultyChange > 300) {
|
||||||
|
difficultyChange = 300;
|
||||||
|
}
|
||||||
|
if (difficultyChange < -75) {
|
||||||
|
difficultyChange = -75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const timeAvgDiff = difficultyChange * 0.1;
|
const timeAvgDiff = difficultyChange * 0.1;
|
||||||
|
|
||||||
let timeAvgMins = 10;
|
let timeAvgMins = 10;
|
||||||
if (timeAvgDiff > 0 ){
|
if (timeAvgDiff > 0) {
|
||||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
timeAvgMins -= Math.abs(timeAvgDiff);
|
||||||
} else {
|
} else {
|
||||||
timeAvgMins += Math.abs(timeAvgDiff);
|
timeAvgMins += Math.abs(timeAvgDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
const remainingBlocks = 2016 - blocksInEpoch;
|
const timeAvg = timeAvgMins * 60;
|
||||||
const timeAvgSeconds = timeAvgMins * 60;
|
const remainingTime = remainingBlocks * timeAvg;
|
||||||
const remainingTime = remainingBlocks * timeAvgSeconds;
|
const estimatedRetargetDate = remainingTime + now;
|
||||||
const estimatedRetargetDate = (remainingTime + now);
|
|
||||||
const totalTime = estimatedRetargetDate-DATime;
|
|
||||||
const progressPercent = 100 - ((remainingTime * 100) / totalTime);
|
|
||||||
|
|
||||||
const result={
|
const result = {
|
||||||
progressPercent,
|
progressPercent,
|
||||||
difficultyChange,
|
difficultyChange,
|
||||||
estimatedRetargetDate,
|
estimatedRetargetDate,
|
||||||
remainingBlocks,
|
remainingBlocks,
|
||||||
remainingTime,
|
remainingTime,
|
||||||
previousRetarget,
|
previousRetarget,
|
||||||
}
|
nextRetargetHeight,
|
||||||
|
timeAvg,
|
||||||
|
};
|
||||||
res.json(result);
|
res.json(result);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,15 +1,14 @@
|
|||||||
FROM node:12-buster-slim AS builder
|
FROM node:16.10.0-buster-slim AS builder
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
RUN apt-get install -y build-essential python3 pkg-config
|
RUN apt-get install -y build-essential python3 pkg-config
|
||||||
RUN npm ci --production
|
RUN npm install
|
||||||
RUN npm i typescript
|
|
||||||
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}
|
||||||
|
|||||||
18
docker/scripts/get_image_digest.sh
Executable file
18
docker/scripts/get_image_digest.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
VERSION=$1
|
||||||
|
IMAGE=""
|
||||||
|
|
||||||
|
if [ -z "${VERSION}" ]; then
|
||||||
|
echo "no version provided (i.e, v2.2.0), using latest tag"
|
||||||
|
VERSION="latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for package in frontend backend; do
|
||||||
|
PACKAGE=mempool/"$package"
|
||||||
|
IMAGE="$PACKAGE":"$VERSION"
|
||||||
|
HASH=`docker pull $IMAGE > /dev/null && docker inspect $IMAGE | sed -n '/RepoDigests/{n;p;}' | grep -o '[0-9a-f]\{64\}'`
|
||||||
|
if [ -n "${HASH}" ]; then
|
||||||
|
echo "$IMAGE"@sha256:"$HASH"
|
||||||
|
fi
|
||||||
|
done
|
||||||
4
frontend/.gitignore
vendored
4
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
|
||||||
@@ -59,3 +60,6 @@ generated-config.js
|
|||||||
cypress/videos
|
cypress/videos
|
||||||
cypress/screenshots
|
cypress/screenshots
|
||||||
|
|
||||||
|
# Base index
|
||||||
|
src/index.html
|
||||||
|
|
||||||
|
|||||||
@@ -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,17 +215,17 @@
|
|||||||
"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
|
||||||
},
|
},
|
||||||
"local-prod": {
|
"local-prod": {
|
||||||
"proxyConfig": "proxy.prod.conf.json",
|
"proxyConfig": "proxy.conf.js",
|
||||||
"disableHostCheck": true,
|
"disableHostCheck": true,
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"verbose": false
|
"verbose": false
|
||||||
@@ -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,4 +1,7 @@
|
|||||||
describe('Bisq', () => {
|
describe('Bisq', () => {
|
||||||
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
|
const basePath = (baseModule === 'bisq') ? '' : '/bisq';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
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');
|
||||||
@@ -20,62 +23,66 @@ describe('Bisq', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
if (baseModule === 'mempool' || baseModule === 'bisq') {
|
||||||
cy.visit('/bisq');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the transactions screen', () => {
|
it('loads the dashboard', () => {
|
||||||
cy.visit('/bisq');
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
|
||||||
cy.get('.table > tr').should('have.length', 50);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the blocks screen', () => {
|
it('loads the transactions screen', () => {
|
||||||
cy.visit('/bisq');
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
||||||
cy.wait('@blocks');
|
cy.get('.table > tr').should('have.length', 50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the blocks screen', () => {
|
||||||
|
cy.visit(`${basePath}`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
|
cy.wait('@blocks');
|
||||||
|
cy.get('tbody tr').should('have.length', 10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the stats screen', () => {
|
||||||
|
cy.visit(`${basePath}`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||||
|
cy.wait('@stats');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the api screen', () => {
|
||||||
|
cy.visit(`${basePath}`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
||||||
|
cy.get('.card').should('have.length.at.least', 1);
|
||||||
|
cy.get('.card').first().click();
|
||||||
|
cy.get('.card-body');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows blocks pagination with 5 pages (desktop)', () => {
|
||||||
|
cy.viewport(760, 800);
|
||||||
|
cy.visit(`${basePath}/blocks`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
cy.get('tbody tr').should('have.length', 10);
|
cy.get('tbody tr').should('have.length', 10);
|
||||||
|
// 5 pages + 4 buttons = 9 buttons
|
||||||
|
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 9);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the stats screen', () => {
|
it('shows blocks pagination with 3 pages (mobile)', () => {
|
||||||
cy.visit('/bisq');
|
cy.viewport(669, 800);
|
||||||
cy.waitForSkeletonGone();
|
cy.visit(`${basePath}/blocks`);
|
||||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
cy.waitForSkeletonGone();
|
||||||
cy.wait('@stats');
|
cy.get('tbody tr').should('have.length', 10);
|
||||||
|
// 3 pages + 4 buttons = 7 buttons
|
||||||
|
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
it('loads the api screen', () => {
|
}
|
||||||
cy.visit('/bisq');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
|
||||||
cy.get('.card').should('have.length.at.least', 1);
|
|
||||||
cy.get('.card').first().click();
|
|
||||||
cy.get('.card-body');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows blocks pagination with 5 pages (desktop)', () => {
|
|
||||||
cy.viewport(760, 800);
|
|
||||||
cy.visit('/bisq/blocks');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('tbody tr').should('have.length', 10);
|
|
||||||
// 5 pages + 4 buttons = 9 buttons
|
|
||||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 9);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows blocks pagination with 3 pages (mobile)', () => {
|
|
||||||
cy.viewport(669, 800);
|
|
||||||
cy.visit('/bisq/blocks');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('tbody tr').should('have.length', 10);
|
|
||||||
// 3 pages + 4 buttons = 7 buttons
|
|
||||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
describe('Liquid', () => {
|
describe('Liquid', () => {
|
||||||
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
|
const basePath = (baseModule === 'liquid') ? '' : '/liquid';
|
||||||
|
|
||||||
beforeEach(() => {
|
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');
|
||||||
@@ -13,137 +16,138 @@ describe('Liquid', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
if (baseModule === 'mempool' || baseModule === 'liquid') {
|
||||||
cy.visit('/liquid');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the blocks page', () => {
|
it('check first mempool block after skeleton loads', () => {
|
||||||
cy.visit('/liquid/blocks');
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads a specific block page', () => {
|
|
||||||
cy.visit('/liquid/block/7e1369a23a5ab861e7bdede2aadcccae4ea873ffd9caf11c7c5541eb5bcdff54');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the graphs page', () => {
|
|
||||||
cy.visit('/liquid/graphs');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the tv page - desktop', () => {
|
|
||||||
cy.visit('/liquid');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
|
||||||
cy.wait(1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the graphs page - mobile', () => {
|
|
||||||
cy.visit('/liquid');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
|
||||||
cy.viewport('iphone-6');
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.get('.tv-only').should('not.exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('assets', () => {
|
|
||||||
it('shows the assets screen', () => {
|
|
||||||
cy.visit('/liquid');
|
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
cy.get('table tr').should('have.length', 5);
|
});
|
||||||
|
|
||||||
|
it('loads the dashboard', () => {
|
||||||
|
cy.visit(`${basePath}`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the blocks page', () => {
|
||||||
|
cy.visit(`${basePath}/blocks`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads a specific block page', () => {
|
||||||
|
cy.visit(`${basePath}/block/7e1369a23a5ab861e7bdede2aadcccae4ea873ffd9caf11c7c5541eb5bcdff54`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the graphs page', () => {
|
||||||
|
cy.visit(`${basePath}/graphs`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the tv page - desktop', () => {
|
||||||
|
cy.visit(`${basePath}`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows searching assets', () => {
|
it('loads the graphs page - mobile', () => {
|
||||||
cy.visit('/liquid');
|
cy.visit(`${basePath}`)
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
|
cy.viewport('iphone-6');
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('.tv-only').should('not.exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('assets', () => {
|
||||||
|
it('shows the assets screen', () => {
|
||||||
|
cy.visit(`${basePath}/assets`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('table tr').should('have.length.at.least', 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows searching assets', () => {
|
||||||
|
cy.visit(`${basePath}/assets`);
|
||||||
|
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('shows a specific asset ID', () => {
|
it('shows a specific asset ID', () => {
|
||||||
cy.visit('/liquid');
|
cy.visit(`${basePath}/assets`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
cy.get('.container-xl input').click().type('Liquid AUD').then(() => {
|
||||||
cy.get('.container-xl input').click().type('Liquid CAD').then(() => {
|
cy.get('table tr td:nth-of-type(1) a').click();
|
||||||
cy.get('table tr td:nth-of-type(4) a').click();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows a specific asset issuance TX', () => {
|
|
||||||
cy.visit('/liquid');
|
describe('unblinded TX', () => {
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
it('should not show an unblinding error message for regular txs', () => {
|
||||||
cy.get('.container-xl input').click().type('Liquid CAD').then(() => {
|
cy.visit(`${basePath}/tx/82a479043ec3841e0d3f829afc8df4f0e2bbd675a13f013ea611b2fde0027d45`);
|
||||||
cy.get('table tr td:nth-of-type(5) a').click();
|
cy.waitForSkeletonGone();
|
||||||
});
|
cy.get('.error-unblinded' ).should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show unblinded TX', () => {
|
||||||
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#table-tx-vin tr').should('have.class', 'assetBox');
|
||||||
|
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show empty unblinded TX', () => {
|
||||||
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#table-tx-vin tr').should('have.class', '');
|
||||||
|
cy.get('#table-tx-vout tr').should('have.class', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show invalid unblinded TX hex', () => {
|
||||||
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=123`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#table-tx-vin tr').should('have.class', '');
|
||||||
|
cy.get('#table-tx-vout tr').should('have.class', '');
|
||||||
|
cy.get('.error-unblinded' ).contains('Error: Invalid blinding data (invalid hex)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show first unblinded vout', () => {
|
||||||
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#table-tx-vout tr:first-child()').should('have.class', 'assetBox');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show second unblinded vout', () => {
|
||||||
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||||
|
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show invalid error unblinded TX', () => {
|
||||||
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3c`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||||
|
cy.get('.error-unblinded' ).contains('Error: Invalid blinding data.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows asset peg in/out and burn transactions', () => {
|
||||||
|
cy.visit(`${basePath}/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#table-tx-vout tr').not('.assetBox');
|
||||||
|
cy.get('#table-tx-vin tr').not('.assetBox');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prevents regressing issue #644', () => {
|
||||||
|
cy.visit(`${basePath}/tx/393b890966f305e7c440fcfb12a13f51a7a9011cc59ff5f14f6f93214261bd82`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
|
}
|
||||||
describe('unblinded TX', () => {
|
|
||||||
it('show unblinded TX', () => {
|
|
||||||
cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('#table-tx-vin tr').should('have.class', 'assetBox');
|
|
||||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show empty unblinded TX', () => {
|
|
||||||
cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('#table-tx-vin tr').should('have.class', '');
|
|
||||||
cy.get('#table-tx-vout tr').should('have.class', '');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show invalid unblinded TX hex', () => {
|
|
||||||
cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=123');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('#table-tx-vin tr').should('have.class', '');
|
|
||||||
cy.get('#table-tx-vout tr').should('have.class', '');
|
|
||||||
cy.get('.error-unblinded' ).contains('Error: Invalid blinding data (invalid hex)');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show first unblinded vout', () => {
|
|
||||||
cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('#table-tx-vout tr:first-child()').should('have.class', 'assetBox');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show second unblinded vout', () => {
|
|
||||||
cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a');
|
|
||||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show invalid error unblinded TX', () => {
|
|
||||||
cy.visit('/liquid/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3c');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
|
||||||
cy.get('.error-unblinded' ).contains('Error: Invalid blinding data.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows asset peg in/out and burn transactions', () => {
|
|
||||||
cy.visit('/liquid/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('#table-tx-vout tr').not('.assetBox');
|
|
||||||
cy.get('#table-tx-vin tr').not('.assetBox');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('prevents regressing issue #644', () => {
|
|
||||||
cy.visit('/liquid/tx/393b890966f305e7c440fcfb12a13f51a7a9011cc59ff5f14f6f93214261bd82');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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');
|
||||||
@@ -9,190 +11,424 @@ describe('Mainnet', () => {
|
|||||||
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
||||||
cy.intercept('/resources/pools.json').as('pools');
|
cy.intercept('/resources/pools.json').as('pools');
|
||||||
|
|
||||||
|
// Search Auto Complete
|
||||||
|
cy.intercept('/api/address-prefix/1wiz').as('search-1wiz');
|
||||||
|
cy.intercept('/api/address-prefix/1wizS').as('search-1wizS');
|
||||||
|
cy.intercept('/api/address-prefix/1wizSA').as('search-1wizSA');
|
||||||
|
|
||||||
Cypress.Commands.add('waitForBlockData', () => {
|
Cypress.Commands.add('waitForBlockData', () => {
|
||||||
cy.wait('@tx-outspends');
|
cy.wait('@tx-outspends');
|
||||||
cy.wait('@pools');
|
cy.wait('@pools');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the status screen', () => {
|
if (baseModule === 'mempool') {
|
||||||
cy.visit('/status');
|
|
||||||
cy.get('#mempool-block-0').should('be.visible');
|
|
||||||
cy.get('[id^="bitcoin-block-"]').should('have.length', 8);
|
|
||||||
cy.get('.footer').should('be.visible');
|
|
||||||
cy.get('.row > :nth-child(1)').invoke('text').then((text) => {
|
|
||||||
expect(text).to.match(/Tx vBytes per second:.* vB\/s/);
|
|
||||||
});
|
|
||||||
cy.get('.row > :nth-child(2)').invoke('text').then((text) => {
|
|
||||||
expect(text).to.match(/Unconfirmed:(.*)/);
|
|
||||||
});
|
|
||||||
cy.get('.row > :nth-child(3)').invoke('text').then((text) => {
|
|
||||||
expect(text).to.match(/Mempool size:(.*) (kB|MB) \((\d+) (block|blocks)\)/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads dashboard, drop websocket and reconnect', () => {
|
it('check first mempool block after skeleton loads', () => {
|
||||||
cy.viewport('macbook-16');
|
cy.visit('/');
|
||||||
cy.mockMempoolSocket();
|
cy.waitForSkeletonGone();
|
||||||
cy.visit('/');
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
cy.get('.badge').should('not.exist');
|
|
||||||
dropWebSocket();
|
|
||||||
cy.get('.badge').should('be.visible');
|
|
||||||
cy.get('.badge', {timeout: 25000}).should('not.exist');
|
|
||||||
emitMempoolInfo({
|
|
||||||
'params': {
|
|
||||||
loaded: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
|
||||||
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
|
||||||
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads skeleton when changes between networks', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
|
|
||||||
cy.changeNetwork("testnet");
|
|
||||||
cy.changeNetwork("signet");
|
|
||||||
cy.changeNetwork("liquid");
|
|
||||||
cy.changeNetwork("mainnet");
|
|
||||||
cy.changeNetwork("bisq");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the dashboard with the skeleton blocks', () => {
|
|
||||||
cy.mockMempoolSocket();
|
|
||||||
cy.visit("/");
|
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get(':nth-child(2) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get(':nth-child(3) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get('#mempool-block-0').should('be.visible');
|
|
||||||
cy.get('#mempool-block-1').should('be.visible');
|
|
||||||
cy.get('#mempool-block-2').should('be.visible');
|
|
||||||
|
|
||||||
emitMempoolInfo({
|
|
||||||
'params': {
|
|
||||||
loaded: true
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
it('loads the status screen', () => {
|
||||||
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
cy.visit('/status');
|
||||||
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the blocks screen', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
|
||||||
cy.waitForPageIdle();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the graphs screen', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
|
||||||
cy.wait(1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the tv screen - desktop', () => {
|
|
||||||
cy.viewport('macbook-16');
|
|
||||||
cy.visit('/');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
|
||||||
cy.viewport('macbook-16');
|
|
||||||
cy.get('.chart-holder');
|
|
||||||
cy.get('.blockchain-wrapper').should('be.visible');
|
|
||||||
cy.get('#mempool-block-0').should('be.visible');
|
cy.get('#mempool-block-0').should('be.visible');
|
||||||
});
|
cy.get('[id^="bitcoin-block-"]').should('have.length', 8);
|
||||||
});
|
cy.get('.footer').should('be.visible');
|
||||||
|
cy.get('.row > :nth-child(1)').invoke('text').then((text) => {
|
||||||
it('loads the tv screen - mobile', () => {
|
expect(text).to.match(/Tx vBytes per second:.* vB\/s/);
|
||||||
cy.viewport('iphone-6');
|
|
||||||
cy.visit('/tv');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('.chart-holder');
|
|
||||||
cy.get('.blockchain-wrapper').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the api screen', () => {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
|
||||||
cy.wait(1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('blocks', () => {
|
|
||||||
it('shows empty blocks properly', () => {
|
|
||||||
cy.visit('/block/0000000000000000000bd14f744ef2e006e61c32214670de7eb891a5732ee775');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.waitForPageIdle();
|
|
||||||
cy.get('h2').invoke('text').should('equal', '1 transaction');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('expands and collapses the block details', () => {
|
|
||||||
cy.visit('/block/0');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.waitForPageIdle();
|
|
||||||
cy.get('.btn.btn-outline-info').click().then(() => {
|
|
||||||
cy.get('#details').should('be.visible');
|
|
||||||
});
|
});
|
||||||
|
cy.get('.row > :nth-child(2)').invoke('text').then((text) => {
|
||||||
cy.get('.btn.btn-outline-info').click().then(() => {
|
expect(text).to.match(/Unconfirmed:(.*)/);
|
||||||
cy.get('#details').should('not.be.visible');
|
});
|
||||||
|
cy.get('.row > :nth-child(3)').invoke('text').then((text) => {
|
||||||
|
expect(text).to.match(/Mempool size:(.*) (kB|MB) \((\d+) (block|blocks)\)/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('shows blocks with no pagination', () => {
|
|
||||||
cy.visit('/block/00000000000000000001ba40caf1ad4cec0ceb77692662315c151953bfd7c4c4');
|
it('loads dashboard, drop websocket and reconnect', () => {
|
||||||
cy.waitForSkeletonGone();
|
cy.viewport('macbook-16');
|
||||||
cy.waitForPageIdle();
|
cy.mockMempoolSocket();
|
||||||
cy.get('.block-tx-title h2').invoke('text').should('equal', '19 transactions');
|
cy.visit('/');
|
||||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 5);
|
cy.get('.badge').should('not.exist');
|
||||||
|
dropWebSocket();
|
||||||
|
cy.get('.badge').should('be.visible');
|
||||||
|
cy.get('.badge', {timeout: 25000}).should('not.exist');
|
||||||
|
emitMempoolInfo({
|
||||||
|
'params': {
|
||||||
|
loaded: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
||||||
|
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
||||||
|
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports pagination on the block screen', () => {
|
it('loads the dashboard', () => {
|
||||||
// 41 txs
|
cy.visit('/');
|
||||||
cy.visit('/block/00000000000000000009f9b7b0f63ad50053ad12ec3b7f5ca951332f134f83d8');
|
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('.pagination-container a').invoke('text').then((text1) => {
|
});
|
||||||
cy.get('.active + li').first().click().then(() => {
|
|
||||||
|
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', () => {
|
||||||
|
it('allows searching for partial Bitcoin addresses', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.get('.search-box-container > .form-control').type('1wiz').then(() => {
|
||||||
|
cy.wait('@search-1wiz');
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.search-box-container > .form-control').type('S').then(() => {
|
||||||
|
cy.wait('@search-1wizS');
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.search-box-container > .form-control').type('A').then(() => {
|
||||||
|
cy.wait('@search-1wizSA');
|
||||||
|
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/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
['BC1PQYQSZQ', 'bc1PqYqSzQ'].forEach((searchTerm) => {
|
||||||
|
it(`allows searching for partial case insensitive bech32m 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/bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsyjer9e');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
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('keyboard events', () => {
|
||||||
|
it('loads first blockchain blocks visible and keypress arrow right', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.blockchain-blocks-0 > a').click().then(() => {
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.document().right();
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads first blockchain blocks visible and keypress arrow left', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.blockchain-blocks-0 > a').click().then(() => {
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.document().left();
|
||||||
|
cy.get('.title-block h1').invoke('text').should('equal', 'Next block');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads last blockchain blocks and keypress arrow right', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.blockchain-blocks-4 > a').click().then(() => {
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
|
||||||
|
// block 6
|
||||||
|
cy.document().right();
|
||||||
|
cy.wait(5000);
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
|
||||||
|
// block 7
|
||||||
|
cy.document().right();
|
||||||
|
cy.wait(5000);
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
|
||||||
|
// block 8 - last visible block
|
||||||
|
cy.document().right();
|
||||||
|
cy.wait(5000);
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
|
||||||
|
// block 9 - not visible at the blochchain blocks visible block
|
||||||
|
cy.document().right();
|
||||||
|
cy.wait(5000);
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads genesis block and keypress arrow right', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/block/0');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.waitForPageIdle();
|
cy.waitForPageIdle();
|
||||||
cy.get('.header-bg.box > a').invoke('text').then((text2) => {
|
|
||||||
expect(text1).not.to.eq(text2);
|
cy.document().right();
|
||||||
|
cy.wait(5000);
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads genesis block and keypress arrow left', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/block/0');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
|
||||||
|
cy.document().left();
|
||||||
|
cy.wait(5000);
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('mouse events', () => {
|
||||||
|
it('loads first blockchain blocks visible and click on the arrow right', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.blockchain-blocks-0 > a').click().then(() => {
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').click().then(() => {
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads genesis block and click on the arrow left', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/block/0');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist');
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').click().then(() => {
|
||||||
|
cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
|
cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows blocks pagination with 5 pages (desktop)', () => {
|
|
||||||
cy.viewport(760, 800);
|
it('loads skeleton when changes between networks', () => {
|
||||||
cy.visit('/block/000000000000000000049281946d26fcba7d99fdabc1feac524bc3a7003d69b3').then(() => {
|
cy.visit('/');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.waitForPageIdle();
|
|
||||||
});
|
cy.changeNetwork("testnet");
|
||||||
|
cy.changeNetwork("signet");
|
||||||
// 5 pages + 4 buttons = 9 buttons
|
cy.changeNetwork("liquid");
|
||||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 9);
|
cy.changeNetwork("mainnet");
|
||||||
|
cy.changeNetwork("bisq");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows blocks pagination with 3 pages (mobile)', () => {
|
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||||
cy.viewport(669, 800);
|
cy.mockMempoolSocket();
|
||||||
cy.visit('/block/000000000000000000049281946d26fcba7d99fdabc1feac524bc3a7003d69b3').then(() => {
|
cy.visit("/");
|
||||||
cy.waitForSkeletonGone();
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||||
|
cy.get(':nth-child(2) > #bitcoin-block-0').should('be.visible');
|
||||||
|
cy.get(':nth-child(3) > #bitcoin-block-0').should('be.visible');
|
||||||
|
cy.get('#mempool-block-0').should('be.visible');
|
||||||
|
cy.get('#mempool-block-1').should('be.visible');
|
||||||
|
cy.get('#mempool-block-2').should('be.visible');
|
||||||
|
|
||||||
|
emitMempoolInfo({
|
||||||
|
'params': {
|
||||||
|
loaded: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
||||||
|
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
||||||
|
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the blocks screen', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
||||||
cy.waitForPageIdle();
|
cy.waitForPageIdle();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3 pages + 4 buttons = 7 buttons
|
|
||||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
it('loads the graphs screen', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.get('.chart-holder');
|
||||||
|
cy.get('.blockchain-wrapper').should('be.visible');
|
||||||
|
cy.get('#mempool-block-0').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the tv screen - mobile', () => {
|
||||||
|
cy.viewport('iphone-6');
|
||||||
|
cy.visit('/tv');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.chart-holder');
|
||||||
|
cy.get('.blockchain-wrapper').should('not.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the api screen', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('blocks', () => {
|
||||||
|
it('shows empty blocks properly', () => {
|
||||||
|
cy.visit('/block/0000000000000000000bd14f744ef2e006e61c32214670de7eb891a5732ee775');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.get('h2').invoke('text').should('equal', '1 transaction');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('expands and collapses the block details', () => {
|
||||||
|
cy.visit('/block/0');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.get('.btn.btn-outline-info').click().then(() => {
|
||||||
|
cy.get('#details').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.btn.btn-outline-info').click().then(() => {
|
||||||
|
cy.get('#details').should('not.be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('shows blocks with no pagination', () => {
|
||||||
|
cy.visit('/block/00000000000000000001ba40caf1ad4cec0ceb77692662315c151953bfd7c4c4');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.get('.block-tx-title h2').invoke('text').should('equal', '19 transactions');
|
||||||
|
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports pagination on the block screen', () => {
|
||||||
|
// 41 txs
|
||||||
|
cy.visit('/block/00000000000000000009f9b7b0f63ad50053ad12ec3b7f5ca951332f134f83d8');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.pagination-container a').invoke('text').then((text1) => {
|
||||||
|
cy.get('.active + li').first().click().then(() => {
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
cy.get('.header-bg.box > a').invoke('text').then((text2) => {
|
||||||
|
expect(text1).not.to.eq(text2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows blocks pagination with 5 pages (desktop)', () => {
|
||||||
|
cy.viewport(760, 800);
|
||||||
|
cy.visit('/block/000000000000000000049281946d26fcba7d99fdabc1feac524bc3a7003d69b3').then(() => {
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5 pages + 4 buttons = 9 buttons
|
||||||
|
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 9);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows blocks pagination with 3 pages (mobile)', () => {
|
||||||
|
cy.viewport(669, 800);
|
||||||
|
cy.visit('/block/000000000000000000049281946d26fcba7d99fdabc1feac524bc3a7003d69b3').then(() => {
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.waitForPageIdle();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3 pages + 4 buttons = 7 buttons
|
||||||
|
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
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');
|
||||||
@@ -8,119 +10,130 @@ describe('Signet', () => {
|
|||||||
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
|
||||||
cy.visit('/signet');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the dashboard with the skeleton blocks', () => {
|
if (baseModule === 'mempool') {
|
||||||
cy.mockMempoolSocket();
|
it('loads the dashboard', () => {
|
||||||
cy.visit("/signet");
|
cy.visit('/signet');
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
cy.waitForSkeletonGone();
|
||||||
cy.get(':nth-child(2) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get(':nth-child(3) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get('#mempool-block-0').should('be.visible');
|
|
||||||
cy.get('#mempool-block-1').should('be.visible');
|
|
||||||
cy.get('#mempool-block-2').should('be.visible');
|
|
||||||
|
|
||||||
emitMempoolInfo({
|
|
||||||
'params': {
|
|
||||||
"network": "signet"
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
|
||||||
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
|
||||||
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the blocks screen', () => {
|
it('check first mempool block after skeleton loads', () => {
|
||||||
cy.visit('/signet');
|
cy.visit('/');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
cy.wait(1000);
|
});
|
||||||
});
|
|
||||||
});
|
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||||
|
cy.mockMempoolSocket();
|
||||||
it('loads the graphs screen', () => {
|
cy.visit("/signet");
|
||||||
cy.visit('/signet');
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||||
cy.waitForSkeletonGone();
|
cy.get(':nth-child(2) > #bitcoin-block-0').should('be.visible');
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
cy.get(':nth-child(3) > #bitcoin-block-0').should('be.visible');
|
||||||
cy.wait(1000);
|
cy.get('#mempool-block-0').should('be.visible');
|
||||||
});
|
cy.get('#mempool-block-1').should('be.visible');
|
||||||
});
|
cy.get('#mempool-block-2').should('be.visible');
|
||||||
|
|
||||||
describe('tv mode', () => {
|
emitMempoolInfo({
|
||||||
it('loads the tv screen - desktop', () => {
|
'params': {
|
||||||
cy.viewport('macbook-16');
|
"network": "signet"
|
||||||
cy.visit('/signet');
|
}
|
||||||
cy.waitForSkeletonGone();
|
});
|
||||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
|
||||||
cy.get('.chart-holder').should('be.visible');
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
||||||
cy.get('#mempool-block-0').should('be.visible');
|
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
||||||
cy.get('.tv-only').should('not.exist');
|
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
it('loads the blocks screen', () => {
|
||||||
it('loads the tv screen - mobile', () => {
|
cy.visit('/signet');
|
||||||
cy.visit('/signet');
|
cy.waitForSkeletonGone();
|
||||||
cy.waitForSkeletonGone();
|
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
||||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
cy.wait(1000);
|
||||||
cy.viewport('iphone-8');
|
});
|
||||||
cy.get('.chart-holder').should('be.visible');
|
});
|
||||||
//TODO: Remove comment when the bug is fixed
|
|
||||||
//cy.get('#mempool-block-0').should('be.visible');
|
it('loads the graphs screen', () => {
|
||||||
cy.get('.tv-only').should('not.exist');
|
cy.visit('/signet');
|
||||||
});
|
cy.waitForSkeletonGone();
|
||||||
});
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
});
|
cy.wait(1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
it('loads the api screen', () => {
|
|
||||||
cy.visit('/signet');
|
describe('tv mode', () => {
|
||||||
cy.waitForSkeletonGone();
|
it('loads the tv screen - desktop', () => {
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
cy.viewport('macbook-16');
|
||||||
cy.wait(1000);
|
cy.visit('/signet');
|
||||||
});
|
cy.waitForSkeletonGone();
|
||||||
});
|
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||||
|
cy.get('.chart-holder').should('be.visible');
|
||||||
describe('blocks', () => {
|
cy.get('#mempool-block-0').should('be.visible');
|
||||||
it('shows empty blocks properly', () => {
|
cy.get('.tv-only').should('not.exist');
|
||||||
cy.visit('/signet/block/00000133d54e4589f6436703b067ec23209e0a21b8a9b12f57d0592fd85f7a42');
|
});
|
||||||
cy.waitForSkeletonGone();
|
});
|
||||||
cy.get('h2').invoke('text').should('equal', '1 transaction');
|
|
||||||
});
|
it('loads the tv screen - mobile', () => {
|
||||||
|
cy.visit('/signet');
|
||||||
it('expands and collapses the block details', () => {
|
cy.waitForSkeletonGone();
|
||||||
cy.visit('/signet/block/0');
|
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||||
cy.waitForSkeletonGone();
|
cy.viewport('iphone-8');
|
||||||
cy.get('.btn.btn-outline-info').click().then(() => {
|
cy.get('.chart-holder').should('be.visible');
|
||||||
cy.get('#details').should('be.visible');
|
//TODO: Remove comment when the bug is fixed
|
||||||
});
|
//cy.get('#mempool-block-0').should('be.visible');
|
||||||
|
cy.get('.tv-only').should('not.exist');
|
||||||
cy.get('.btn.btn-outline-info').click().then(() => {
|
});
|
||||||
cy.get('#details').should('not.be.visible');
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('shows blocks with no pagination', () => {
|
it('loads the api screen', () => {
|
||||||
cy.visit('/signet/block/00000078f920a96a69089877b934ce7fd009ab55e3170920a021262cb258e7cc');
|
cy.visit('/signet');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('h2').invoke('text').should('equal', '13 transactions');
|
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
||||||
cy.get('ul.pagination').first().children().should('have.length', 5);
|
cy.wait(1000);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('supports pagination on the block screen', () => {
|
|
||||||
// 43 txs
|
describe('blocks', () => {
|
||||||
cy.visit('/signet/block/00000094bd52f73bdbfc4bece3a94c21fec2dc968cd54210496e69e4059d66a6');
|
it('shows empty blocks properly', () => {
|
||||||
cy.waitForSkeletonGone();
|
cy.visit('/signet/block/00000133d54e4589f6436703b067ec23209e0a21b8a9b12f57d0592fd85f7a42');
|
||||||
cy.get('.header-bg.box > a').invoke('text').then((text1) => {
|
cy.waitForSkeletonGone();
|
||||||
cy.get('.active + li').first().click().then(() => {
|
cy.get('h2').invoke('text').should('equal', '1 transaction');
|
||||||
cy.get('.header-bg.box > a').invoke('text').then((text2) => {
|
});
|
||||||
expect(text1).not.to.eq(text2);
|
|
||||||
});
|
it('expands and collapses the block details', () => {
|
||||||
});
|
cy.visit('/signet/block/0');
|
||||||
});
|
cy.waitForSkeletonGone();
|
||||||
});
|
cy.get('.btn.btn-outline-info').click().then(() => {
|
||||||
});
|
cy.get('#details').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.btn.btn-outline-info').click().then(() => {
|
||||||
|
cy.get('#details').should('not.be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows blocks with no pagination', () => {
|
||||||
|
cy.visit('/signet/block/00000078f920a96a69089877b934ce7fd009ab55e3170920a021262cb258e7cc');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('h2').invoke('text').should('equal', '13 transactions');
|
||||||
|
cy.get('ul.pagination').first().children().should('have.length', 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports pagination on the block screen', () => {
|
||||||
|
// 43 txs
|
||||||
|
cy.visit('/signet/block/00000094bd52f73bdbfc4bece3a94c21fec2dc968cd54210496e69e4059d66a6');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.header-bg.box > a').invoke('text').then((text1) => {
|
||||||
|
cy.get('.active + li').first().click().then(() => {
|
||||||
|
cy.get('.header-bg.box > a').invoke('text').then((text2) => {
|
||||||
|
expect(text1).not.to.eq(text2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { emitMempoolInfo } from "../../support/websocket";
|
import { confirmAddress, emitMempoolInfo, sendWsMock, showNewTx, startTrackingAddress } from "../../support/websocket";
|
||||||
|
|
||||||
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
|
|
||||||
describe('Testnet', () => {
|
describe('Testnet', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -8,115 +10,127 @@ describe('Testnet', () => {
|
|||||||
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
if (baseModule === 'mempool') {
|
||||||
cy.visit('/testnet');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the dashboard with the skeleton blocks', () => {
|
it('loads the dashboard', () => {
|
||||||
cy.mockMempoolSocket();
|
cy.visit('/testnet');
|
||||||
cy.visit("/signet");
|
cy.waitForSkeletonGone();
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get(':nth-child(2) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get(':nth-child(3) > #bitcoin-block-0').should('be.visible');
|
|
||||||
cy.get('#mempool-block-0').should('be.visible');
|
|
||||||
cy.get('#mempool-block-1').should('be.visible');
|
|
||||||
cy.get('#mempool-block-2').should('be.visible');
|
|
||||||
|
|
||||||
emitMempoolInfo({
|
|
||||||
'params': {
|
|
||||||
loaded: true
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
|
||||||
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
|
||||||
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the blocks screen', () => {
|
it('check first mempool block after skeleton loads', () => {
|
||||||
cy.visit('/testnet');
|
cy.visit('/');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
cy.wait(1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the graphs screen', () => {
|
|
||||||
cy.visit('/testnet');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
|
||||||
cy.wait(1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('tv mode', () => {
|
|
||||||
it('loads the tv screen - desktop', () => {
|
|
||||||
cy.viewport('macbook-16');
|
|
||||||
cy.visit('/testnet');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.get('.tv-only').should('not.exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('loads the tv screen - mobile', () => {
|
|
||||||
cy.visit('/testnet');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
|
||||||
cy.viewport('iphone-6');
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.get('.tv-only').should('not.exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('loads the api screen', () => {
|
|
||||||
cy.visit('/testnet');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
|
||||||
cy.wait(1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('blocks', () => {
|
|
||||||
it('shows empty blocks properly', () => {
|
|
||||||
cy.visit('/testnet/block/0');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('h2').invoke('text').should('equal', '1 transaction');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('expands and collapses the block details', () => {
|
|
||||||
cy.visit('/testnet/block/0');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('.btn.btn-outline-info').click().then(() => {
|
|
||||||
cy.get('#details').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get('.btn.btn-outline-info').click().then(() => {
|
|
||||||
cy.get('#details').should('not.be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows blocks with no pagination', () => {
|
|
||||||
cy.visit('/testnet/block/000000000000002f8ce27716e74ecc7ad9f7b5101fed12d09e28bb721b9460ea');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('h2').invoke('text').should('equal', '11 transactions');
|
|
||||||
cy.get('ul.pagination').first().children().should('have.length', 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports pagination on the block screen', () => {
|
|
||||||
// 48 txs
|
|
||||||
cy.visit('/testnet/block/000000000000002ca3878ebd98b313a1c2d531f2e70a6575d232ca7564dea7a9');
|
|
||||||
cy.waitForSkeletonGone();
|
|
||||||
cy.get('.header-bg.box > a').invoke('text').then((text1) => {
|
|
||||||
cy.get('.active + li').first().click().then(() => {
|
|
||||||
cy.get('.header-bg.box > a').invoke('text').then((text2) => {
|
|
||||||
expect(text1).not.to.eq(text2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||||
|
cy.mockMempoolSocket();
|
||||||
|
cy.visit("/testnet");
|
||||||
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||||
|
cy.get(':nth-child(2) > #bitcoin-block-0').should('be.visible');
|
||||||
|
cy.get(':nth-child(3) > #bitcoin-block-0').should('be.visible');
|
||||||
|
cy.get('#mempool-block-0').should('be.visible');
|
||||||
|
cy.get('#mempool-block-1').should('be.visible');
|
||||||
|
cy.get('#mempool-block-2').should('be.visible');
|
||||||
|
|
||||||
|
emitMempoolInfo({
|
||||||
|
'params': {
|
||||||
|
loaded: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
||||||
|
cy.get(':nth-child(2) > #bitcoin-block-0').should('not.exist');
|
||||||
|
cy.get(':nth-child(3) > #bitcoin-block-0').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the blocks screen', () => {
|
||||||
|
cy.visit('/testnet');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the graphs screen', () => {
|
||||||
|
cy.visit('/testnet');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tv mode', () => {
|
||||||
|
it('loads the tv screen - desktop', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/testnet');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('.tv-only').should('not.exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads the tv screen - mobile', () => {
|
||||||
|
cy.visit('/testnet');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||||
|
cy.viewport('iphone-6');
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('.tv-only').should('not.exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('loads the api screen', () => {
|
||||||
|
cy.visit('/testnet');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
||||||
|
cy.wait(1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('blocks', () => {
|
||||||
|
it('shows empty blocks properly', () => {
|
||||||
|
cy.visit('/testnet/block/0');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('h2').invoke('text').should('equal', '1 transaction');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('expands and collapses the block details', () => {
|
||||||
|
cy.visit('/testnet/block/0');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.btn.btn-outline-info').click().then(() => {
|
||||||
|
cy.get('#details').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('.btn.btn-outline-info').click().then(() => {
|
||||||
|
cy.get('#details').should('not.be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows blocks with no pagination', () => {
|
||||||
|
cy.visit('/testnet/block/000000000000002f8ce27716e74ecc7ad9f7b5101fed12d09e28bb721b9460ea');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('h2').invoke('text').should('equal', '11 transactions');
|
||||||
|
cy.get('ul.pagination').first().children().should('have.length', 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports pagination on the block screen', () => {
|
||||||
|
// 48 txs
|
||||||
|
cy.visit('/testnet/block/000000000000002ca3878ebd98b313a1c2d531f2e70a6575d232ca7564dea7a9');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.header-bg.box > a').invoke('text').then((text1) => {
|
||||||
|
cy.get('.active + li').first().click().then(() => {
|
||||||
|
cy.get('.header-bg.box > a').invoke('text').then((text2) => {
|
||||||
|
expect(text1).not.to.eq(text2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,14 +42,24 @@
|
|||||||
// -- This will overwrite an existing command --
|
// -- This will overwrite an existing command --
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
import 'cypress-wait-until';
|
import 'cypress-wait-until';
|
||||||
import { PageIdleDetector } from './PageIdleDetector';
|
import { PageIdleDetector } from './PageIdleDetector';
|
||||||
import { mockWebSocket } from './websocket';
|
import { mockWebSocket } from './websocket';
|
||||||
|
|
||||||
|
/* global Cypress */
|
||||||
|
const codes = {
|
||||||
|
ArrowLeft: 37,
|
||||||
|
ArrowUp: 38,
|
||||||
|
ArrowRight: 39,
|
||||||
|
ArrowDown: 40
|
||||||
|
}
|
||||||
|
|
||||||
Cypress.Commands.add('waitForSkeletonGone', () => {
|
Cypress.Commands.add('waitForSkeletonGone', () => {
|
||||||
cy.waitUntil(() => {
|
cy.waitUntil(() => {
|
||||||
return Cypress.$('.skeleton-loader').length === 0;
|
return Cypress.$('.skeleton-loader').length === 0;
|
||||||
}, { verbose: true, description: "waitForSkeletonGone", errorMsg: "skeleton loaders never went away", timeout: 7000, interval: 50});
|
}, { verbose: true, description: "waitForSkeletonGone", errorMsg: "skeleton loaders never went away", timeout: 15000, interval: 50});
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add(
|
Cypress.Commands.add(
|
||||||
@@ -75,3 +85,63 @@ Cypress.Commands.add('changeNetwork', (network: "testnet"|"signet"|"liquid"|"bis
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/bahmutov/cypress-arrows/blob/8f0303842a343550fbeaf01528d01d1ff213b70c/src/index.js
|
||||||
|
function keydownCommand ($el, key) {
|
||||||
|
const message = `sending the "${key}" keydown event`
|
||||||
|
const log = Cypress.log({
|
||||||
|
name: `keydown: ${key}`,
|
||||||
|
message: message,
|
||||||
|
consoleProps: function () {
|
||||||
|
return {
|
||||||
|
Subject: $el
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const e = $el.createEvent('KeyboardEvent')
|
||||||
|
|
||||||
|
Object.defineProperty(e, 'key', {
|
||||||
|
get: function () {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.defineProperty(e, 'keyCode', {
|
||||||
|
get: function () {
|
||||||
|
return this.keyCodeVal
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Object.defineProperty(e, 'which', {
|
||||||
|
get: function () {
|
||||||
|
return this.keyCodeVal
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var metaKey = false
|
||||||
|
|
||||||
|
Object.defineProperty(e, 'metaKey', {
|
||||||
|
get: function () {
|
||||||
|
return metaKey
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.defineProperty(e, 'shiftKey', {
|
||||||
|
get: function () {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
e.keyCodeVal = codes[key]
|
||||||
|
|
||||||
|
e.initKeyboardEvent('keydown', true, true,
|
||||||
|
$el.defaultView, false, false, false, false, e.keyCodeVal, e.keyCodeVal)
|
||||||
|
|
||||||
|
$el.dispatchEvent(e)
|
||||||
|
log.snapshot().end()
|
||||||
|
return $el
|
||||||
|
}
|
||||||
|
|
||||||
|
Cypress.Commands.add('keydown', { prevSubject: "dom" }, keydownCommand)
|
||||||
|
Cypress.Commands.add('left', { prevSubject: "dom" }, $el => keydownCommand($el, 'ArrowLeft'))
|
||||||
|
Cypress.Commands.add('right', { prevSubject: "dom" }, $el => keydownCommand($el, 'ArrowRight'))
|
||||||
|
Cypress.Commands.add('up', { prevSubject: "dom" }, $el => keydownCommand($el, 'ArrowUp'))
|
||||||
|
Cypress.Commands.add('down', { prevSubject: "dom" }, $el => keydownCommand($el, 'ArrowDown'))
|
||||||
@@ -21,6 +21,16 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const indexFilePath = configContent.BASE_MODULE ? 'src/index.' + configContent.BASE_MODULE + '.html' : 'src/index.mempool.html';
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.copyFileSync(indexFilePath, 'src/index.html');
|
||||||
|
console.log('Copied ' + indexFilePath + ' to src/index.html');
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error copying the index file');
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const packageJson = fs.readFileSync('package.json');
|
const packageJson = fs.readFileSync('package.json');
|
||||||
packetJsonVersion = JSON.parse(packageJson).version;
|
packetJsonVersion = JSON.parse(packageJson).version;
|
||||||
|
|||||||
@@ -8,5 +8,8 @@
|
|||||||
"KEEP_BLOCKS_AMOUNT": 8,
|
"KEEP_BLOCKS_AMOUNT": 8,
|
||||||
"NGINX_PROTOCOL": "http",
|
"NGINX_PROTOCOL": "http",
|
||||||
"NGINX_HOSTNAME": "127.0.0.1",
|
"NGINX_HOSTNAME": "127.0.0.1",
|
||||||
"NGINX_PORT": "80"
|
"NGINX_PORT": "80",
|
||||||
|
"MEMPOOL_BLOCKS_AMOUNT": 8,
|
||||||
|
"BLOCK_WEIGHT_UNITS": 4000000,
|
||||||
|
"BASE_MODULE": "mempool"
|
||||||
}
|
}
|
||||||
|
|||||||
25215
frontend/package-lock.json
generated
25215
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.1",
|
"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,16 +30,18 @@
|
|||||||
"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",
|
||||||
"build-mempool.js": "tsc | browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
|
"build-mempool.js": "tsc | browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e",
|
"e2e": "npm run generate-config && ng e2e",
|
||||||
"e2e:ci": "npm run cypress:run:ci",
|
"e2e:ci": "npm run cypress:run:ci",
|
||||||
"config:defaults": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config",
|
"config:defaults:mempool": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=mempool && npm run generate-config",
|
||||||
|
"config:defaults:liquid": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=liquid && npm run generate-config",
|
||||||
|
"config:defaults:bisq": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 BASE_MODULE=bisq && npm run generate-config",
|
||||||
"dev:ssr": "npm run generate-config && ng run mempool:serve-ssr",
|
"dev:ssr": "npm run generate-config && ng run mempool:serve-ssr",
|
||||||
"serve:ssr": "node server.run.js",
|
"serve:ssr": "node server.run.js",
|
||||||
"build:ssr": "npm run build && ng run mempool:server:production && ./node_modules/typescript/bin/tsc server.run.ts",
|
"build:ssr": "npm run build && ng run mempool:server:production && ./node_modules/typescript/bin/tsc server.run.ts",
|
||||||
@@ -51,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",
|
||||||
@@ -86,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",
|
||||||
@@ -99,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": "^7.7.0",
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
79
frontend/proxy.conf.js
Normal file
79
frontend/proxy.conf.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
let PROXY_CONFIG;
|
||||||
|
let configContent;
|
||||||
|
|
||||||
|
const CONFIG_FILE_NAME = 'mempool-frontend-config.json';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rawConfig = fs.readFileSync(CONFIG_FILE_NAME);
|
||||||
|
configContent = JSON.parse(rawConfig);
|
||||||
|
console.log(`${CONFIG_FILE_NAME} file found, using provided config`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
if (e.code !== 'ENOENT') {
|
||||||
|
throw new Error(e);
|
||||||
|
} else {
|
||||||
|
console.log(`${CONFIG_FILE_NAME} file not found, using default config`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PROXY_CONFIG = [
|
||||||
|
{
|
||||||
|
context: ['*',
|
||||||
|
'/api/**', '!/api/v1/ws',
|
||||||
|
'!/bisq', '!/bisq/**', '!/bisq/',
|
||||||
|
'!/liquid', '!/liquid/**', '!/liquid/',
|
||||||
|
'/testnet/api/**', '/signet/api/**'
|
||||||
|
],
|
||||||
|
target: "https://mempool.space",
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/api/v1/ws'],
|
||||||
|
target: "https://mempool.space",
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/api/bisq**', '/bisq/api/**'],
|
||||||
|
target: "https://bisq.markets",
|
||||||
|
pathRewrite: {
|
||||||
|
"^/api/bisq/": "/bisq/api"
|
||||||
|
},
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: ['/api/liquid**', '/liquid/api/**'],
|
||||||
|
target: "https://liquid.network",
|
||||||
|
pathRewrite: {
|
||||||
|
"^/api/liquid/": "/liquid/api"
|
||||||
|
},
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -1,107 +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/": {
|
|
||||||
"target": "http://localhost:50001/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/liquid/api/": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/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';
|
||||||
|
|||||||
@@ -20,12 +20,18 @@ import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-poli
|
|||||||
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
|
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
|
||||||
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 { 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,
|
||||||
@@ -94,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,
|
||||||
@@ -163,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,
|
||||||
@@ -225,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,
|
||||||
@@ -303,7 +321,7 @@ const browserWindow = window || {};
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const browserWindowEnv = browserWindow.__env || {};
|
const browserWindowEnv = browserWindow.__env || {};
|
||||||
|
|
||||||
if (browserWindowEnv && browserWindowEnv.OFFICIAL_BISQ_MARKETS) {
|
if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') {
|
||||||
routes = [{
|
routes = [{
|
||||||
path: '',
|
path: '',
|
||||||
component: BisqMasterPageComponent,
|
component: BisqMasterPageComponent,
|
||||||
@@ -311,6 +329,97 @@ if (browserWindowEnv && browserWindowEnv.OFFICIAL_BISQ_MARKETS) {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||||
|
routes = [{
|
||||||
|
path: '',
|
||||||
|
component: LiquidMasterPageComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: StartComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: DashboardComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tx/:id',
|
||||||
|
component: TransactionComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'block/:id',
|
||||||
|
component: BlockComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'mempool-block/:id',
|
||||||
|
component: MempoolBlockComponent
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'blocks',
|
||||||
|
component: LatestBlocksComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'graphs',
|
||||||
|
component: StatisticsComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'address/:id',
|
||||||
|
component: AddressComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'asset/:id',
|
||||||
|
component: AssetComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'assets',
|
||||||
|
component: AssetsComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'api',
|
||||||
|
component: ApiDocsComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'about',
|
||||||
|
component: AboutComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'terms-of-service',
|
||||||
|
component: TermsOfServiceComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'privacy-policy',
|
||||||
|
component: PrivacyPolicyComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'trademark-policy',
|
||||||
|
component: TrademarkPolicyComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'sponsor',
|
||||||
|
component: SponsorComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tv',
|
||||||
|
component: TelevisionComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'status',
|
||||||
|
component: StatusViewComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '**',
|
||||||
|
redirectTo: ''
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes, {
|
imports: [RouterModule.forRoot(routes, {
|
||||||
initialNavigation: 'enabled',
|
initialNavigation: 'enabled',
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -22,19 +23,21 @@ import { AddressLabelsComponent } from './components/address-labels/address-labe
|
|||||||
import { MempoolBlocksComponent } from './components/mempool-blocks/mempool-blocks.component';
|
import { MempoolBlocksComponent } from './components/mempool-blocks/mempool-blocks.component';
|
||||||
import { MasterPageComponent } from './components/master-page/master-page.component';
|
import { MasterPageComponent } from './components/master-page/master-page.component';
|
||||||
import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component';
|
import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component';
|
||||||
|
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
|
||||||
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';
|
||||||
@@ -44,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, 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';
|
||||||
@@ -54,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: [
|
||||||
@@ -61,6 +65,7 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
|
|||||||
AboutComponent,
|
AboutComponent,
|
||||||
MasterPageComponent,
|
MasterPageComponent,
|
||||||
BisqMasterPageComponent,
|
BisqMasterPageComponent,
|
||||||
|
LiquidMasterPageComponent,
|
||||||
TelevisionComponent,
|
TelevisionComponent,
|
||||||
BlockchainComponent,
|
BlockchainComponent,
|
||||||
StartComponent,
|
StartComponent,
|
||||||
@@ -76,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,
|
||||||
@@ -93,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' }),
|
||||||
@@ -104,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,
|
||||||
@@ -132,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);
|
||||||
@@ -145,5 +156,7 @@ export class AppModule {
|
|||||||
library.addIcons(faSortUp);
|
library.addIcons(faSortUp);
|
||||||
library.addIcons(faCaretUp);
|
library.addIcons(faCaretUp);
|
||||||
library.addIcons(faCaretDown);
|
library.addIcons(faCaretDown);
|
||||||
|
library.addIcons(faAngleRight);
|
||||||
|
library.addIcons(faAngleLeft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -20,16 +20,14 @@
|
|||||||
<th i18n="Asset ticker header">Ticker</th>
|
<th i18n="Asset ticker header">Ticker</th>
|
||||||
<th class="d-none d-md-block" i18n="Asset Issuer Domain header">Issuer domain</th>
|
<th class="d-none d-md-block" i18n="Asset Issuer Domain header">Issuer domain</th>
|
||||||
<th i18n="Asset ID header">Asset ID</th>
|
<th i18n="Asset ID header">Asset ID</th>
|
||||||
<th class="d-none d-lg-block" i18n="Asset issuance transaction header">Issuance TX</th>
|
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let asset of filteredAssets; trackBy: trackByAsset">
|
<tr *ngFor="let asset of filteredAssets; trackBy: trackByAsset">
|
||||||
<td class="td-name">{{ asset.name }}</td>
|
<td class="td-name"><a [routerLink]="['/asset/' | relativeUrl, asset.asset_id]">{{ asset.name }}</a></td>
|
||||||
<td>{{ asset.ticker }}</td>
|
<td>{{ asset.ticker }}</td>
|
||||||
<td class="d-none d-md-block"><a *ngIf="asset.entity" target="_blank" href="{{ 'http://' + asset.entity.domain }}">{{ asset.entity.domain }}</a></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>
|
||||||
<td class="d-none d-lg-block"><ng-template [ngIf]="asset.issuance_txin"><a [routerLink]="['/tx/' | relativeUrl, asset.issuance_txin.txid]">{{ asset.issuance_txin.txid | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.issuance_txin.txid"></app-clipboard></ng-template></td>
|
</tr>
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -47,16 +45,14 @@
|
|||||||
<th i18n="Asset ticker header">Ticker</th>
|
<th i18n="Asset ticker header">Ticker</th>
|
||||||
<th i18n="Asset Issuer Domain header">Issuer domain</th>
|
<th i18n="Asset Issuer Domain header">Issuer domain</th>
|
||||||
<th i18n="Asset ID header">Asset ID</th>
|
<th i18n="Asset ID header">Asset ID</th>
|
||||||
<th i18n="Asset issuance transaction header">Issuance TX</th>
|
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let dummy of [0,0,0]">
|
<tr *ngFor="let dummy of [0,0,0,0,0,0,0,0,0,0]">
|
||||||
<td><span class="skeleton-loader"></span></td>
|
<td><span class="skeleton-loader"></span></td>
|
||||||
<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>
|
||||||
<td class="d-none d-lg-block"><span class="skeleton-loader"></span></td>
|
</tr>
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -72,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export class AssetsComponent implements OnInit {
|
|||||||
const start = (this.page - 1) * this.itemsPerPage;
|
const start = (this.page - 1) * this.itemsPerPage;
|
||||||
if (searchText.length ) {
|
if (searchText.length ) {
|
||||||
const filteredAssets = this.assetsCache.filter((asset) => asset.name.toLowerCase().indexOf(searchText.toLowerCase()) > -1
|
const filteredAssets = this.assetsCache.filter((asset) => asset.name.toLowerCase().indexOf(searchText.toLowerCase()) > -1
|
||||||
|| asset.ticker.toLowerCase().indexOf(searchText.toLowerCase()) > -1);
|
|| (asset.ticker || '').toLowerCase().indexOf(searchText.toLowerCase()) > -1);
|
||||||
this.assets = filteredAssets;
|
this.assets = filteredAssets;
|
||||||
return filteredAssets.slice(start, this.itemsPerPage + start);
|
return filteredAssets.slice(start, this.itemsPerPage + start);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div class="container-info">
|
<div class="container-info">
|
||||||
<h1>
|
<h1>
|
||||||
<ng-template [ngIf]="stateService.env.OFFICIAL_BISQ_MARKETS" [ngIfElse]="nonOfficialMarkets" i18n="Bisq All Markets">Markets</ng-template>
|
<ng-template [ngIf]="stateService.env.BASE_MODULE === 'bisq'" [ngIfElse]="nonOfficialMarkets" i18n="Bisq All Markets">Markets</ng-template>
|
||||||
<ng-template #nonOfficialMarkets i18n="Bisq Bitcoin Markets">Bitcoin Markets</ng-template>
|
<ng-template #nonOfficialMarkets i18n="Bisq Bitcoin Markets">Bitcoin Markets</ng-template>
|
||||||
</h1>
|
</h1>
|
||||||
<ng-container *ngIf="{ value: (tickers$ | async) } as tickers">
|
<ng-container *ngIf="{ value: (tickers$ | async) } as tickers">
|
||||||
|
|||||||
@@ -17,19 +17,6 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
|
||||||
overflow: scroll;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
scrollbar-width: none;
|
|
||||||
font-size: 13px;
|
|
||||||
@media(min-width: 576px){
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-info{
|
.container-info{
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ export class BisqDashboardComponent implements OnInit {
|
|||||||
const newTickers = [];
|
const newTickers = [];
|
||||||
for (const t in tickers) {
|
for (const t in tickers) {
|
||||||
|
|
||||||
if (!this.stateService.env.OFFICIAL_BISQ_MARKETS) {
|
if (this.stateService.env.BASE_MODULE !== 'bisq') {
|
||||||
const pair = t.split('_');
|
const pair = t.split('_');
|
||||||
if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) {
|
if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) {
|
||||||
continue;
|
continue;
|
||||||
@@ -106,7 +106,7 @@ export class BisqDashboardComponent implements OnInit {
|
|||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([trades, markets]) => {
|
map(([trades, markets]) => {
|
||||||
if (!this.stateService.env.OFFICIAL_BISQ_MARKETS) {
|
if (this.stateService.env.BASE_MODULE !== 'bisq') {
|
||||||
trades = trades.filter((trade) => {
|
trades = trades.filter((trade) => {
|
||||||
const pair = trade.market.split('_');
|
const pair = trade.market.split('_');
|
||||||
return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1);
|
return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1);
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title text-center">
|
<h5 class="card-title text-center">
|
||||||
<ng-template [ngIf]="stateService.env.OFFICIAL_BISQ_MARKETS" [ngIfElse]="nonOfficialMarkets" i18n="Bisq All Markets">Markets</ng-template>
|
<ng-template [ngIf]="stateService.env.BASE_MODULE === 'bisq'" [ngIfElse]="nonOfficialMarkets" i18n="Bisq All Markets">Markets</ng-template>
|
||||||
<ng-template #nonOfficialMarkets i18n="Bisq Bitcoin Markets">Bitcoin Markets</ng-template>
|
<ng-template #nonOfficialMarkets i18n="Bisq Bitcoin Markets">Bitcoin Markets</ng-template>
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-language-selector *ngIf="!stateService.env.OFFICIAL_BISQ_MARKETS"></app-language-selector>
|
<app-language-selector *ngIf="stateService.env.BASE_MODULE !== 'bisq'"></app-language-selector>
|
||||||
|
|
||||||
<div class="text-small text-center mt-3">
|
<div class="text-small text-center mt-3">
|
||||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||||
|
|||||||
@@ -18,15 +18,44 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
font-size: 13px;
|
overflow: scroll;
|
||||||
@media(min-width: 576px){
|
scrollbar-width: none;
|
||||||
font-size: 16px;
|
font-size: 13px;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@media(min-width: 576px){
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
thead th{
|
||||||
|
text-align: right;
|
||||||
|
&:first-child {
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
&::-webkit-scrollbar {
|
&:nth-child(3) {
|
||||||
|
display: none;
|
||||||
|
@media(min-width: 1100px){
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
text-align: right;
|
||||||
|
&:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
&:nth-child(3) {
|
||||||
display: none;
|
display: none;
|
||||||
|
@media(min-width: 1100px){
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
@@ -41,44 +70,43 @@
|
|||||||
background-color: #1d1f31;
|
background-color: #1d1f31;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
color: #4a68b9;
|
color: #4a68b9;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-block {
|
.info-block {
|
||||||
float: left;
|
float: left;
|
||||||
width: 350px;
|
width: 350px;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #2d3348;
|
background-color: #2d3348;
|
||||||
height: 1.1rem;
|
height: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-warning {
|
.bg-warning {
|
||||||
background-color: #b58800 !important;
|
background-color: #b58800 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton-loader {
|
.skeleton-loader {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
&.shorter {
|
&.shorter {
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-padding {
|
.more-padding {
|
||||||
padding: 1.25rem 2rem 1.25rem 2rem;
|
padding: 1.25rem 2rem 1.25rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph-card {
|
.graph-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
height: 385px;
|
height: 385px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ export class BisqMainDashboardComponent implements OnInit {
|
|||||||
const newTickers = [];
|
const newTickers = [];
|
||||||
for (const t in tickers) {
|
for (const t in tickers) {
|
||||||
|
|
||||||
if (!this.stateService.env.OFFICIAL_BISQ_MARKETS) {
|
if (this.stateService.env.BASE_MODULE !== 'bisq') {
|
||||||
const pair = t.split('_');
|
const pair = t.split('_');
|
||||||
if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) {
|
if (pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1) {
|
||||||
continue;
|
continue;
|
||||||
@@ -114,7 +114,7 @@ export class BisqMainDashboardComponent implements OnInit {
|
|||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([trades, markets]) => {
|
map(([trades, markets]) => {
|
||||||
if (!this.stateService.env.OFFICIAL_BISQ_MARKETS) {
|
if (this.stateService.env.BASE_MODULE !== 'bisq') {
|
||||||
trades = trades.filter((trade) => {
|
trades = trades.filter((trade) => {
|
||||||
const pair = trade.market.split('_');
|
const pair = trade.market.split('_');
|
||||||
return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1);
|
return !(pair[1] === 'btc' && this.allowCryptoCoins.indexOf(pair[0]) === -1);
|
||||||
|
|||||||
@@ -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,12 +1,38 @@
|
|||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
-ms-overflow-style: none;
|
scrollbar-width: none;
|
||||||
scrollbar-width: none;
|
font-size: 13px;
|
||||||
font-size: 13px;
|
&::-webkit-scrollbar {
|
||||||
@media(min-width: 576px){
|
display: none;
|
||||||
font-size: 16px;
|
}
|
||||||
|
@media(min-width: 576px){
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
thead th{
|
||||||
|
text-align: right;
|
||||||
|
&:first-child{
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
&::-webkit-scrollbar {
|
&:nth-child(2) {
|
||||||
|
display: none;
|
||||||
|
@media(min-width: 1100px){
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
text-align: right;
|
||||||
|
&:first-child{
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
&:nth-child(2) {
|
||||||
display: none;
|
display: none;
|
||||||
|
@media(min-width: 1100px){
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||||
<td *ngIf="!isLoadingTx; else loadingTxFee">
|
<td *ngIf="!isLoadingTx; else loadingTxFee">
|
||||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span class="symbol">sat/vB</span>
|
{{ tx.fee / (tx.weight / 4) | feeRounding }} <span class="symbol">sat/vB</span>
|
||||||
|
|
||||||
<app-tx-fee-rating [tx]="tx"></app-tx-fee-rating>
|
<app-tx-fee-rating [tx]="tx"></app-tx-fee-rating>
|
||||||
</td>
|
</td>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -85,7 +85,11 @@ export class BisqTransactionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tx.version) {
|
if (tx.version) {
|
||||||
this.router.navigate(['/tx/', this.txId], { state: { data: tx, bsqTx: true }});
|
if (this.stateService.env.BASE_MODULE === 'bisq') {
|
||||||
|
window.location.replace('https://mempool.space/tx/' + this.txId);
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/tx/', this.txId], { state: { data: tx, bsqTx: true }});
|
||||||
|
}
|
||||||
return of(null);
|
return of(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -8,10 +8,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="about-text">
|
<div class="about-text" *ngIf="stateService.env.BASE_MODULE === 'mempool'; else marginBox">
|
||||||
<h5><ng-container i18n="about.about-the-project">The Mempool Open Source Project</ng-container><ng-template [ngIf]="locale.substr(0, 2) === 'en'"> ™</ng-template></h5>
|
<h5><ng-container i18n="about.about-the-project">The Mempool Open Source Project</ng-container><ng-template [ngIf]="locale.substr(0, 2) === 'en'"> ™</ng-template></h5>
|
||||||
<p i18n>Building a mempool and blockchain explorer for the Bitcoin community, focusing on the transaction fee market and multi-layer ecosystem, without any advertising, altcoins, or third-party trackers.</p>
|
<p i18n>Building a mempool and blockchain explorer for the Bitcoin community, focusing on the transaction fee market and multi-layer ecosystem, without any advertising, altcoins, or third-party trackers.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-template #marginBox>
|
||||||
|
<div class="no-about-margin"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<div class="social-icons">
|
<div class="social-icons">
|
||||||
<a target="_blank" href="https://github.com/mempool/mempool">
|
<a target="_blank" href="https://github.com/mempool/mempool">
|
||||||
@@ -31,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" />
|
||||||
@@ -47,6 +50,10 @@
|
|||||||
<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.com/" target="_blank" title="Unchained">
|
||||||
|
<img class="image" src="/resources/profile/unchained.svg" />
|
||||||
|
<span>Unchained</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -119,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>
|
||||||
@@ -131,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>
|
||||||
|
|
||||||
@@ -152,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>
|
||||||
|
|||||||
@@ -176,4 +176,8 @@
|
|||||||
.footer-version {
|
.footer-version {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-about-margin {
|
||||||
|
height: 10px;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
@@ -25,7 +26,7 @@ export class AboutComponent implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private stateService: StateService,
|
public stateService: StateService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@Inject(LOCALE_ID) public locale: string,
|
@Inject(LOCALE_ID) public locale: string,
|
||||||
@@ -37,11 +38,18 @@ 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() {
|
||||||
if (this.officialMempoolSpace) {
|
if (this.officialMempoolSpace && this.stateService.env.BASE_MODULE === 'mempool') {
|
||||||
this.router.navigateByUrl('/sponsor');
|
this.router.navigateByUrl('/sponsor');
|
||||||
} else {
|
} else {
|
||||||
this.showNavigateToSponsor = true;
|
this.showNavigateToSponsor = true;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
@@ -18,10 +19,14 @@
|
|||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
<table class="table table-borderless table-striped">
|
<table class="table table-borderless table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<tr *ngIf="addressInfo && addressInfo.unconfidential">
|
||||||
|
<td i18n="address.unconfidential">Unconfidential</td>
|
||||||
|
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">{{ addressInfo.unconfidential }}</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></td>
|
||||||
|
</tr>
|
||||||
<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>
|
||||||
@@ -30,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>
|
||||||
@@ -46,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>
|
||||||
|
|
||||||
@@ -81,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>
|
||||||
@@ -110,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>
|
||||||
@@ -118,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>
|
||||||
@@ -132,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { AudioService } from 'src/app/services/audio.service';
|
|||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { of, merge, Subscription, Observable } from 'rxjs';
|
import { of, merge, Subscription, Observable } from 'rxjs';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
|
import { AddressInformation } from 'src/app/interfaces/node-api.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-address',
|
selector: 'app-address',
|
||||||
@@ -23,15 +24,16 @@ 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>;
|
||||||
|
addressInfo: null | AddressInformation = null;
|
||||||
|
|
||||||
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[];
|
||||||
@@ -67,8 +69,12 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
this.address = null;
|
this.address = null;
|
||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
this.transactions = null;
|
this.transactions = null;
|
||||||
|
this.addressInfo = null;
|
||||||
document.body.scrollTo(0, 0);
|
document.body.scrollTo(0, 0);
|
||||||
this.addressString = params.get('id') || '';
|
this.addressString = params.get('id') || '';
|
||||||
|
if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(this.addressString)) {
|
||||||
|
this.addressString = this.addressString.toLowerCase();
|
||||||
|
}
|
||||||
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
|
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
|
||||||
|
|
||||||
return merge(
|
return merge(
|
||||||
@@ -92,10 +98,20 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
filter((address) => !!address),
|
filter((address) => !!address),
|
||||||
|
tap((address: Address) => {
|
||||||
|
if (this.stateService.network === 'liquid' && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) {
|
||||||
|
this.apiService.validateAddress$(address.address)
|
||||||
|
.subscribe((addressInfo) => {
|
||||||
|
this.addressInfo = addressInfo;
|
||||||
|
this.websocketService.startTrackAddress(addressInfo.unconfidential);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.websocketService.startTrackAddress(address.address);
|
||||||
|
}
|
||||||
|
}),
|
||||||
switchMap((address) => {
|
switchMap((address) => {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.updateChainStats();
|
this.updateChainStats();
|
||||||
this.websocketService.startTrackAddress(address.address);
|
|
||||||
this.isLoadingAddress = false;
|
this.isLoadingAddress = false;
|
||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
return this.electrsApiService.getAddressTransactions$(address.address);
|
return this.electrsApiService.getAddressTransactions$(address.address);
|
||||||
@@ -126,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;
|
||||||
@@ -161,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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -184,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;
|
||||||
@@ -194,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,6 +1,6 @@
|
|||||||
import { Component, OnInit, Input, ChangeDetectionStrategy } from '@angular/core';
|
import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy } from '@angular/core';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-amount',
|
selector: 'app-amount',
|
||||||
@@ -8,11 +8,13 @@ import { Observable } from 'rxjs';
|
|||||||
styleUrls: ['./amount.component.scss'],
|
styleUrls: ['./amount.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class AmountComponent implements OnInit {
|
export class AmountComponent implements OnInit, OnDestroy {
|
||||||
conversions$: Observable<any>;
|
conversions$: Observable<any>;
|
||||||
viewFiat$: Observable<boolean>;
|
viewFiat$: Observable<boolean>;
|
||||||
network = '';
|
network = '';
|
||||||
|
|
||||||
|
stateSubscription: Subscription;
|
||||||
|
|
||||||
@Input() satoshis: number;
|
@Input() satoshis: number;
|
||||||
@Input() digitsInfo = '1.8-8';
|
@Input() digitsInfo = '1.8-8';
|
||||||
@Input() noFiat = false;
|
@Input() noFiat = false;
|
||||||
@@ -24,7 +26,13 @@ export class AmountComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.viewFiat$ = this.stateService.viewFiat$.asObservable();
|
this.viewFiat$ = this.stateService.viewFiat$.asObservable();
|
||||||
this.conversions$ = this.stateService.conversions$.asObservable();
|
this.conversions$ = this.stateService.conversions$.asObservable();
|
||||||
this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
this.stateSubscription = this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.stateSubscription) {
|
||||||
|
this.stateSubscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,39 +1,39 @@
|
|||||||
<div class="code">
|
<div class="code">
|
||||||
<ul ngbNav #navCodeTemplate="ngbNav" class="nav-tabs code-tab">
|
<ul ngbNav #navCodeTemplate="ngbNav" class="nav-tabs code-tab">
|
||||||
<li ngbNavItem *ngIf="code.codeSample.curl">
|
<li ngbNavItem *ngIf="code.codeTemplate.curl && method !== 'websocket'">
|
||||||
<a ngbNavLink>cURL</a>
|
<a ngbNavLink>cURL</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCurl(code.codeSample.curl)"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCurlTemplate(code)"></app-clipboard></div>
|
||||||
<pre><code [innerText]="wrapCurl(code.codeSample.curl)"></code></pre>
|
<pre><code [innerText]="wrapCurlTemplate(code)"></code></pre>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li ngbNavItem>
|
<li ngbNavItem>
|
||||||
<a ngbNavLink>CommonJS</a>
|
<a ngbNavLink>CommonJS</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCommonJS(code.codeSample.commonJS)"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCommonJS(code)"></app-clipboard></div>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<a href="https://github.com/mempool/mempool.js" target="_blank">github repository</a>
|
<a [href]="npmGithubLink()" target="_blank">github repository</a>
|
||||||
</div>
|
</div>
|
||||||
<pre><code [innerText]="wrapCommonJS(code.codeSample.commonJS)"></code></pre>
|
<pre><code [innerText]="wrapCommonJS(code)"></code></pre>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li ngbNavItem>
|
<li ngbNavItem>
|
||||||
<a ngbNavLink>ES Module</a>
|
<a ngbNavLink>ES Module</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="subtitle"><ng-container i18n="API Docs install lib">Install Package</ng-container> <app-clipboard [text]="esModuleInstall"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs install lib">Install Package</ng-container> <app-clipboard [text]="wrapImportTemplate()"></app-clipboard></div>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<a href="https://github.com/mempool/mempool.js" target="_blank">github repository</a>
|
<a [href]="npmGithubLink()" target="_blank">github repository</a>
|
||||||
<a href="https://www.npmjs.org/package/@mempool/mempool.js" target="_blank">npm package</a>
|
<a [href]="npmModuleLink()" target="_blank">npm package</a>
|
||||||
</div>
|
</div>
|
||||||
<pre><code [innerText]="esModuleInstall"></code></pre>
|
<pre><code [innerText]="wrapImportTemplate()"></code></pre>
|
||||||
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapESmodule(code.codeSample.esModule)"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapEsModule(code)"></app-clipboard></div>
|
||||||
<pre><code [innerText]="wrapESmodule(code.codeSample.esModule)"></code></pre>
|
<pre><code [innerText]="wrapEsModule(code)"></code></pre>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div [ngbNavOutlet]="navCodeTemplate"></div>
|
<div [ngbNavOutlet]="navCodeTemplate"></div>
|
||||||
<div *ngIf="code.responseSample" class="response">
|
<div *ngIf="code.codeTemplate && wrapResponse(code) !== ''" class="response">
|
||||||
<div class="subtitle"><ng-container i18n="API Docs API response">Response</ng-container> <app-clipboard [text]="code.responseSample"></app-clipboard></div>
|
<div class="subtitle"><ng-container i18n="API Docs API response">Response</ng-container> <app-clipboard [text]="wrapResponse(code)"></app-clipboard></div>
|
||||||
<pre><code [innerText]="code.responseSample"></code></pre>
|
<pre><code [innerText]="wrapResponse(code)"></code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,69 +1,189 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { Env, StateService } from 'src/app/services/state.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-code-template',
|
selector: 'app-code-template',
|
||||||
templateUrl: './code-template.component.html',
|
templateUrl: './code-template.component.html',
|
||||||
styleUrls: ['./code-template.component.scss']
|
styleUrls: ['./code-template.component.scss']
|
||||||
})
|
})
|
||||||
export class CodeTemplateComponent {
|
export class CodeTemplateComponent implements OnInit {
|
||||||
@Input() network: string;
|
@Input() network: string;
|
||||||
@Input() code: {
|
@Input() code: any;
|
||||||
codeSample: {
|
@Input() hostname: string;
|
||||||
esModule: string;
|
@Input() method: 'get' | 'post' | 'websocket' = 'get';
|
||||||
commonJS: string;
|
env: Env;
|
||||||
curl: string;
|
|
||||||
},
|
|
||||||
responseSample: string;
|
|
||||||
};
|
|
||||||
hostname = document.location.hostname;
|
|
||||||
esModuleInstall = `# npm
|
|
||||||
npm install @mempool/mempool.js --save
|
|
||||||
|
|
||||||
# yarn
|
|
||||||
yarn add @mempool/mempool.js`;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private stateService: StateService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
normalizeCodeHostname(code: string) {
|
ngOnInit(): void {
|
||||||
let codeText: string;
|
this.env = this.stateService.env;
|
||||||
if (this.network === 'bisq' || this.network === 'liquid'){
|
}
|
||||||
codeText = code.replace('%{1}', this.network);
|
|
||||||
}else{
|
npmGithubLink(){
|
||||||
codeText = code.replace('%{1}', 'bitcoin');
|
let npmLink = `https://github.com/mempool/mempool.js`;
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`;
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`;
|
||||||
|
}
|
||||||
|
return npmLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
npmModuleLink() {
|
||||||
|
let npmLink = `https://www.npmjs.org/package/@mempool/mempool.js`;
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`;
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`;
|
||||||
|
}
|
||||||
|
return npmLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeHostsESModule(codeText: string) {
|
||||||
|
if (this.env.BASE_MODULE === 'mempool') {
|
||||||
|
if (['liquid', 'bisq'].includes(this.network)) {
|
||||||
|
codeText = codeText.replace('%{0}', this.network);
|
||||||
|
} else {
|
||||||
|
codeText = codeText.replace('%{0}', 'bitcoin');
|
||||||
|
}
|
||||||
|
if(['', 'main', 'liquid', 'bisq'].includes(this.network)) {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}'
|
||||||
|
});`);
|
||||||
|
} else {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}',
|
||||||
|
network: '${this.network}'
|
||||||
|
});`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
|
codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`);
|
||||||
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`);
|
||||||
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
}
|
}
|
||||||
return codeText;
|
return codeText;
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapESmodule(code: string) {
|
normalizeHostsCommonJS(codeText: string) {
|
||||||
let codeText = this.normalizeCodeHostname(code);
|
if (this.env.BASE_MODULE === 'mempool') {
|
||||||
|
if (['liquid', 'bisq'].includes(this.network)) {
|
||||||
if (this.network && this.network !== 'mainnet') {
|
codeText = codeText.replace('%{0}', this.network);
|
||||||
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
} else {
|
||||||
hostname: '${this.hostname}/${this.network}'
|
codeText = codeText.replace('%{0}', 'bitcoin');
|
||||||
});` );
|
}
|
||||||
|
if(['', 'main', 'liquid', 'bisq'].includes(this.network)) {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}'
|
||||||
|
});`);
|
||||||
|
} else {
|
||||||
|
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||||
|
hostname: '${document.location.hostname}',
|
||||||
|
network: '${this.network}'
|
||||||
|
});`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return `import mempoolJS from "@mempool/mempool.js";
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
|
codeText = codeText.replace('} = mempoolJS();', ` = bisqJS();`);
|
||||||
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
codeText = codeText.replace('} = mempoolJS();', ` = liquidJS();`);
|
||||||
|
codeText = codeText.replace('{ %{0}: ', '');
|
||||||
|
}
|
||||||
|
return codeText;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapEsModule(code: any) {
|
||||||
|
let codeText: string;
|
||||||
|
if (code.codeTemplate) {
|
||||||
|
codeText = this.normalizeHostsESModule(code.codeTemplate.esModule);
|
||||||
|
|
||||||
|
if(this.network === '' || this.network === 'main') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleMainnet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'testnet') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
let importText = `import mempoolJS from "@mempool/mempool.js";`;
|
||||||
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
|
importText = `import bisqJS from "@mempool/bisq.js";`;
|
||||||
|
}
|
||||||
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
importText = `import liquidJS from "@mempool/liquid.js";`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${importText}
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
${codeText}
|
${codeText}
|
||||||
};
|
};
|
||||||
init();`;
|
init();`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapCommonJS(code: string) {
|
wrapCommonJS(code: any) {
|
||||||
let codeText = this.normalizeCodeHostname(code);
|
let codeText: string;
|
||||||
|
if (code.codeTemplate) {
|
||||||
|
codeText = this.normalizeHostsCommonJS(code.codeTemplate.commonJS);
|
||||||
|
|
||||||
if (this.network && this.network !== 'mainnet') {
|
if(this.network === '' || this.network === 'main') {
|
||||||
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleMainnet.esModule);
|
||||||
hostname: '${this.hostname}/${this.network}'
|
}
|
||||||
});` );
|
if (this.network === 'testnet') {
|
||||||
}
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleTestnet.esModule);
|
||||||
return `<!DOCTYPE html>
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
let importText = `<script src="https://mempool.space/mempool.js"></script>`;
|
||||||
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
|
importText = `<script src="https://bisq.markets/bisq.js"></script>`;
|
||||||
|
}
|
||||||
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
importText = `<script src="https://liquid.network/liquid.js"></script>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultHtml = '<pre id="result"></pre>';
|
||||||
|
if (this.method === 'websocket') {
|
||||||
|
resultHtml = `<h2>Blocks</h2><pre id="result-blocks">Waiting for data</pre><br>
|
||||||
|
<h2>Mempool Info</h2><pre id="result-mempool-info">Waiting for data</pre><br>
|
||||||
|
<h2>Transactions</h2><pre id="result-transactions">Waiting for data</pre><br>
|
||||||
|
<h2>Mempool Blocks</h2><pre id="result-mempool-blocks">Waiting for data</pre><br>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<script src="https://mempool.space/mempool.js"></script>
|
${importText}
|
||||||
<script>
|
<script>
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
${codeText}
|
${codeText}
|
||||||
@@ -71,14 +191,112 @@ init();`;
|
|||||||
init();
|
init();
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body>
|
||||||
|
${resultHtml}
|
||||||
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
}
|
|
||||||
wrapCurl(code: string) {
|
|
||||||
if (this.network && this.network !== 'mainnet') {
|
|
||||||
return code.replace('mempool.space/', `mempool.space/${this.network}/`);
|
|
||||||
}
|
}
|
||||||
return code;
|
}
|
||||||
|
|
||||||
|
wrapImportTemplate() {
|
||||||
|
|
||||||
|
let importTemplate = `# npm
|
||||||
|
npm install @mempool/mempool.js --save
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn add @mempool/mempool.js`;
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'bisq') {
|
||||||
|
importTemplate = `# npm
|
||||||
|
npm install @mempool/bisq.js --save
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn add @mempool/bisq.js`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'liquid') {
|
||||||
|
importTemplate = `# npm
|
||||||
|
npm install @mempool/liquid.js --save
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
yarn add @mempool/liquid.js`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return importTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapCurlTemplate(code: any) {
|
||||||
|
if (code.codeTemplate) {
|
||||||
|
if (this.network === 'testnet') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleTestnet);
|
||||||
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleSignet);
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquid);
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleBisq);
|
||||||
|
}
|
||||||
|
if (this.network === '' || this.network === 'main') {
|
||||||
|
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleMainnet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapResponse(code: any) {
|
||||||
|
if (this.method === 'websocket') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (this.network === 'testnet') {
|
||||||
|
return code.codeSampleTestnet.response;
|
||||||
|
}
|
||||||
|
if (this.network === 'signet') {
|
||||||
|
return code.codeSampleSignet.response;
|
||||||
|
}
|
||||||
|
if (this.network === 'liquid') {
|
||||||
|
return code.codeSampleLiquid.response;
|
||||||
|
}
|
||||||
|
if (this.network === 'bisq') {
|
||||||
|
return code.codeSampleBisq.response;
|
||||||
|
}
|
||||||
|
return code.codeSampleMainnet.response;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceJSPlaceholder(text: string, code: any) {
|
||||||
|
for (let index = 0; index < code.length; index++) {
|
||||||
|
const textReplace = code[index];
|
||||||
|
const indexNumber = index + 1;
|
||||||
|
text = text.replace('%{' + indexNumber + '}', textReplace);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceCurlPlaceholder(curlText: any, code: any) {
|
||||||
|
let text = curlText;
|
||||||
|
for (let index = 0; index < code.curl.length; index++) {
|
||||||
|
const textReplace = code.curl[index];
|
||||||
|
const indexNumber = index + 1;
|
||||||
|
text = text.replace('%{' + indexNumber + '}', textReplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.env.BASE_MODULE === 'mempool') {
|
||||||
|
if (this.network === 'main' || this.network === '') {
|
||||||
|
if (this.method === 'post') {
|
||||||
|
return `curl -X POST -sSLd "${text}"`;
|
||||||
|
}
|
||||||
|
return `curl -sSL "${this.hostname}${text}"`;
|
||||||
|
}
|
||||||
|
if (this.method === 'post') {
|
||||||
|
text = text.replace('/api', `/${this.network}/api`);
|
||||||
|
return `curl -X POST -sSLd "${text}"`;
|
||||||
|
}
|
||||||
|
return `curl -sSL "${this.hostname}/${this.network}${text}"`;
|
||||||
|
}
|
||||||
|
if (this.env.BASE_MODULE !== 'mempool') {
|
||||||
|
return `curl -sSL "${this.hostname}${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,7 +2,7 @@
|
|||||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
||||||
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
||||||
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
||||||
<img [src]="'./resources/bisq-markets.svg'" height="35" width="180" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }">
|
<img src="./resources/bisq/bisq-markets-logo.png" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }">
|
||||||
<div class="connection-badge">
|
<div class="connection-badge">
|
||||||
<div class="badge badge-warning" *ngIf="connectionState.val === 0" i18n="master-page.offline">Offline</div>
|
<div class="badge badge-warning" *ngIf="connectionState.val === 0" i18n="master-page.offline">Offline</div>
|
||||||
<div class="badge badge-warning" *ngIf="connectionState.val === 1" i18n="master-page.reconnecting">Reconnecting...</div>
|
<div class="badge badge-warning" *ngIf="connectionState.val === 1" i18n="master-page.reconnecting">Reconnecting...</div>
|
||||||
@@ -10,15 +10,42 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||||
|
<button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split" aria-haspopup="true">
|
||||||
|
<img src="./resources/bisq-logo.png" style="width: 25px; height: 25px;" class="mr-1">
|
||||||
|
</button>
|
||||||
|
<div ngbDropdownMenu [ngClass]="{'dropdown-menu-right' : isMobile}">
|
||||||
|
<a href="https://mempool.space" ngbDropdownItem class="mainnet"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1"> Mainnet</a>
|
||||||
|
<a href="https://mempool.space/signet" ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet"><img src="./resources/signet-logo.png" style="width: 30px;" class="mr-1"> Signet</a>
|
||||||
|
<a href="https://mempool.space/testnet" ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1"> Testnet</a>
|
||||||
|
<h6 class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
|
||||||
|
<button ngbDropdownItem class="mainnet active" routerLink="/"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
|
||||||
|
<a href="https://liquid.network" ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="navbar-collapse" id="navbarCollapse">
|
<div class="navbar-collapse" id="navbarCollapse">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
|
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
|
||||||
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
|
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
|
||||||
|
<a class="nav-link" [routerLink]="['/transactions']" (click)="collapse()"><fa-icon [icon]="['fas', 'list']" [fixedWidth]="true" i18n-title="master-page.transactions" title="Transactions"></fa-icon></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" routerLinkActive="active">
|
||||||
|
<a class="nav-link" [routerLink]="['/blocks']" (click)="collapse()"><fa-icon [icon]="['fas', 'cubes']" [fixedWidth]="true" i18n-title="master-page.blocks" title="Blocks"></fa-icon></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" routerLinkActive="active">
|
||||||
|
<a class="nav-link" [routerLink]="['/stats']" (click)="collapse()"><fa-icon [icon]="['fas', 'file-alt']" [fixedWidth]="true" i18n-title="master-page.stats" title="Stats"></fa-icon></a>
|
||||||
|
</li>
|
||||||
<li class="nav-item mr-2" routerLinkActive="active">
|
<li class="nav-item mr-2" routerLinkActive="active">
|
||||||
<a class="nav-link" [routerLink]="['/api' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cogs']" [fixedWidth]="true" i18n-title="master-page.api" title="API"></fa-icon></a>
|
<a class="nav-link" [routerLink]="['/api' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cogs']" [fixedWidth]="true" i18n-title="master-page.api" title="API"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" routerLinkActive="active">
|
||||||
|
<a class="nav-link" [routerLink]="['/about']" (click)="collapse()"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" i18n-title="master-page.about" title="About"></fa-icon></a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<app-search-form class="search-form-container" location="top" (searchTriggered)="collapse()"></app-search-form>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ fa-icon {
|
|||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
min-height: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.nav-item {
|
li.nav-item {
|
||||||
|
margin: auto 5px;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 992px) {
|
||||||
.navbar {
|
.navbar {
|
||||||
padding: 0rem 2rem;
|
padding: 0rem 2rem;
|
||||||
}
|
}
|
||||||
@@ -26,17 +28,54 @@ li.nav-item {
|
|||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
li.nav-item {
|
li.nav-item {
|
||||||
|
margin: auto 0px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li.nav-item a {
|
.navbar-nav {
|
||||||
color: #ffffff;
|
background: #212121;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: 0px 0px 15px 0px #000;
|
||||||
|
flex-direction: row;
|
||||||
|
left: 0;
|
||||||
|
justify-content: center;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
font-size: 0.8em;
|
||||||
|
@media (min-width: 375px) {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-nav {
|
|
||||||
flex-direction: row;
|
.navbar-collapse {
|
||||||
justify-content: center;
|
flex-basis: auto;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.navbar-collapse {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
.navbar-brand {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
@@ -81,5 +120,20 @@ nav {
|
|||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items:center;
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.search-form-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navbar-dark .navbar-nav .nav-link {
|
||||||
|
color: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user