Compare commits
303 Commits
v2.3.0-rc2
...
v2.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb525b0bd3 | ||
|
|
53c8cdee83 | ||
|
|
b9067ed912 | ||
|
|
11cc14f5b0 | ||
|
|
4133bf31c6 | ||
|
|
bfeee747c2 | ||
|
|
31fb6f70ab | ||
|
|
cb38258cf7 | ||
|
|
2a16dc5a7f | ||
|
|
20476e1366 | ||
|
|
57b0ccee60 | ||
|
|
80ec15193c | ||
|
|
d61eba8c68 | ||
|
|
4787b6353a | ||
|
|
debcd1808e | ||
|
|
85f471ad08 | ||
|
|
c7fa785346 | ||
|
|
a710934830 | ||
|
|
69e006f640 | ||
|
|
78c32af062 | ||
|
|
9a47191e10 | ||
|
|
ace5da94a4 | ||
|
|
e7f2f75b05 | ||
|
|
5b39ad2130 | ||
|
|
ee1985bb3d | ||
|
|
9caa57e81d | ||
|
|
8797ef261f | ||
|
|
fb9a548dfc | ||
|
|
ad4bfefee7 | ||
|
|
cd9157488f | ||
|
|
b501f7228c | ||
|
|
11483852da | ||
|
|
a6fadc840d | ||
|
|
af8c8a2088 | ||
|
|
573cb8f993 | ||
|
|
d70c610741 | ||
|
|
985d19778f | ||
|
|
2cb50c2351 | ||
|
|
359e111ae4 | ||
|
|
548f38292f | ||
|
|
5f2350b763 | ||
|
|
831cd580e0 | ||
|
|
47a6969dc9 | ||
|
|
29581f325f | ||
|
|
fbce72b7fc | ||
|
|
a894fa5bc0 | ||
|
|
9ac3c420eb | ||
|
|
10df6985fc | ||
|
|
27ce863735 | ||
|
|
d840d79aea | ||
|
|
80dfc81900 | ||
|
|
31c911cb59 | ||
|
|
0d4160b232 | ||
|
|
f0022f6af9 | ||
|
|
a16decfb94 | ||
|
|
ea2a2310a0 | ||
|
|
7f17ade65c | ||
|
|
c8d38740cc | ||
|
|
efffd1a929 | ||
|
|
f0c53a4e5b | ||
|
|
a9c1dc3726 | ||
|
|
2944f0b805 | ||
|
|
f494bd6d6a | ||
|
|
ae2cb05dc5 | ||
|
|
4e322fe006 | ||
|
|
5d06d02d64 | ||
|
|
7eabbe30e6 | ||
|
|
c232f6a11d | ||
|
|
04ffa6d7bb | ||
|
|
d46655e5f4 | ||
|
|
1438300763 | ||
|
|
cce49bdb7e | ||
|
|
fc878b696d | ||
|
|
c09fdb656f | ||
|
|
9ac9eb9cc8 | ||
|
|
ff5367b0e7 | ||
|
|
503adc20dc | ||
|
|
2f5cad9d0a | ||
|
|
871329e0fd | ||
|
|
7825b8d732 | ||
|
|
6bfd9da08c | ||
|
|
ce8518ad58 | ||
|
|
865fe488bf | ||
|
|
467cac7d4d | ||
|
|
3a0fb2015a | ||
|
|
bfb5abaa71 | ||
|
|
6cb2625303 | ||
|
|
2d292e27b9 | ||
|
|
9b6d679739 | ||
|
|
8099349dcc | ||
|
|
b1df17d7a3 | ||
|
|
02798db449 | ||
|
|
4b71cb6e28 | ||
|
|
cee52e69f1 | ||
|
|
a4a8fb64b1 | ||
|
|
0e6cc67c0a | ||
|
|
cc621b10ce | ||
|
|
2eaea44182 | ||
|
|
50734bafbf | ||
|
|
745b7d6f65 | ||
|
|
4ca730697c | ||
|
|
dc06a3f62a | ||
|
|
1e78326ee4 | ||
|
|
45542d5f06 | ||
|
|
0106f44129 | ||
|
|
ba895559bf | ||
|
|
513886f6d2 | ||
|
|
09fe7346bc | ||
|
|
4173486f4d | ||
|
|
d809e85dde | ||
|
|
6414f0045e | ||
|
|
39c5393e3b | ||
|
|
d2cd396c75 | ||
|
|
ccbb28c8a0 | ||
|
|
afbced3f4d | ||
|
|
08f2287def | ||
|
|
5175027948 | ||
|
|
d0cda447c0 | ||
|
|
fd288cd106 | ||
|
|
2d0d7df704 | ||
|
|
c41ac34978 | ||
|
|
47307bc755 | ||
|
|
bfe5d3ae49 | ||
|
|
a060816e2c | ||
|
|
898ff5da23 | ||
|
|
d78d2c0eca | ||
|
|
a08e77ff3e | ||
|
|
1e39eb0fa5 | ||
|
|
5de133ae6a | ||
|
|
d27b125848 | ||
|
|
ad36d53bb5 | ||
|
|
24f76f2f37 | ||
|
|
691bdda523 | ||
|
|
81bb31090e | ||
|
|
cc0a0719b6 | ||
|
|
7dca8ae1a0 | ||
|
|
84027d5568 | ||
|
|
4116186c1a | ||
|
|
358301020f | ||
|
|
642022bfd8 | ||
|
|
70f25b6c9c | ||
|
|
c778e84247 | ||
|
|
4de1d017ad | ||
|
|
61851be23a | ||
|
|
5de949eaed | ||
|
|
de6434a5ba | ||
|
|
c8639ec71d | ||
|
|
e1275c62cc | ||
|
|
be45e88056 | ||
|
|
990ab3da5f | ||
|
|
d1d74ebf37 | ||
|
|
6ab79b3c35 | ||
|
|
4f21fc0d87 | ||
|
|
10c4e47091 | ||
|
|
dd49ff0084 | ||
|
|
853314ba58 | ||
|
|
784e2470df | ||
|
|
350b4922da | ||
|
|
40fb1792f4 | ||
|
|
7ce1cc5103 | ||
|
|
71a4e24900 | ||
|
|
a48c2c07b0 | ||
|
|
d89d7efbe6 | ||
|
|
5ea4b043d9 | ||
|
|
dd4710b602 | ||
|
|
832c0cb3cc | ||
|
|
04216e952a | ||
|
|
951d0f0039 | ||
|
|
706f4bbc55 | ||
|
|
3fd96e412b | ||
|
|
766803ded1 | ||
|
|
504f46cad9 | ||
|
|
fd34761a93 | ||
|
|
96e8f45e5b | ||
|
|
195fae670b | ||
|
|
dd767f9468 | ||
|
|
bc8104eeb4 | ||
|
|
2c61eb6227 | ||
|
|
5d360d4156 | ||
|
|
91e30fbc3c | ||
|
|
5b22e2a000 | ||
|
|
533653e54a | ||
|
|
3dc0dc13ad | ||
|
|
e332789afc | ||
|
|
e4a9fd06b4 | ||
|
|
5845f2380e | ||
|
|
c29311d831 | ||
|
|
252db109bc | ||
|
|
b1c9334119 | ||
|
|
ab04247726 | ||
|
|
e94a85b989 | ||
|
|
a4569788f8 | ||
|
|
b455814e90 | ||
|
|
7afd0f3fe7 | ||
|
|
a2a85469cf | ||
|
|
94488a6029 | ||
|
|
8e4829146a | ||
|
|
08f185525c | ||
|
|
d6b00fe39e | ||
|
|
cec3baeaa4 | ||
|
|
6e59733cac | ||
|
|
c5b705ede7 | ||
|
|
2819e24efe | ||
|
|
5f9bc4497a | ||
|
|
086b14e816 | ||
|
|
958bfe6d25 | ||
|
|
e01ab449cf | ||
|
|
eeb0f403a3 | ||
|
|
9a18019d9d | ||
|
|
bcbc60b456 | ||
|
|
0d14c30892 | ||
|
|
5d8c970351 | ||
|
|
89fede9e48 | ||
|
|
f8a54784d0 | ||
|
|
010381aac4 | ||
|
|
49c30c7237 | ||
|
|
7ba0055c61 | ||
|
|
72b631a4dc | ||
|
|
1a8fd23b05 | ||
|
|
b9a2143a5a | ||
|
|
3ae46e6ba1 | ||
|
|
815fb62e7d | ||
|
|
7a19da560e | ||
|
|
43a1af4509 | ||
|
|
40f1949654 | ||
|
|
cf1471acca | ||
|
|
a2a69e522c | ||
|
|
4939941c88 | ||
|
|
a643f50016 | ||
|
|
836444a1c5 | ||
|
|
24d4b643e5 | ||
|
|
7f045ae5b9 | ||
|
|
96a2c8ab4e | ||
|
|
47103d85d3 | ||
|
|
0af5d733c4 | ||
|
|
bc7bbf5fe5 | ||
|
|
4bc141cd67 | ||
|
|
1c6b3a46c6 | ||
|
|
b41e32915f | ||
|
|
adb5bfe93f | ||
|
|
f4b7bbc91c | ||
|
|
687b4af272 | ||
|
|
e67f552fbd | ||
|
|
28d3f190ff | ||
|
|
2281116504 | ||
|
|
92d745168c | ||
|
|
cf0af20947 | ||
|
|
aee319ed51 | ||
|
|
dc5ced416d | ||
|
|
c9f5002dc2 | ||
|
|
6e4985602e | ||
|
|
11577842a2 | ||
|
|
1cc8a9469a | ||
|
|
7e7dbdbaf2 | ||
|
|
fb152b6d7b | ||
|
|
9e8a741d97 | ||
|
|
2b0d543ce7 | ||
|
|
5e729373bb | ||
|
|
41f3f0ab46 | ||
|
|
37722fe165 | ||
|
|
cbd187d06f | ||
|
|
833dd3ef9d | ||
|
|
bd19496350 | ||
|
|
ec420d8f3e | ||
|
|
9510c7d2ea | ||
|
|
92d2eb9727 | ||
|
|
72f7284311 | ||
|
|
2b3463519a | ||
|
|
00352d7e36 | ||
|
|
63e799b225 | ||
|
|
3c1e264464 | ||
|
|
ff148f15c4 | ||
|
|
a5c7f152aa | ||
|
|
f0be19409f | ||
|
|
936a306dc4 | ||
|
|
8ff5748368 | ||
|
|
271726bf58 | ||
|
|
65e11c1aa1 | ||
|
|
0ccc992e4b | ||
|
|
52397cb341 | ||
|
|
f7a9b691a8 | ||
|
|
8f8c9c1fbc | ||
|
|
9e8ee4fedc | ||
|
|
30ffc0b56e | ||
|
|
053fb4d1b2 | ||
|
|
53cfda533d | ||
|
|
2d557195de | ||
|
|
ed9b46bae0 | ||
|
|
23c9bf489a | ||
|
|
7c89dde5d4 | ||
|
|
87ca5b85d7 | ||
|
|
a2a43f4647 | ||
|
|
9ac77f7113 | ||
|
|
b6215dd54f | ||
|
|
69d5cfe98e | ||
|
|
73d9cd7ab7 | ||
|
|
84a6d29914 | ||
|
|
4633164f99 | ||
|
|
b22b63c9a2 | ||
|
|
40cd2a1ac3 | ||
|
|
e28f5cc403 | ||
|
|
1594fed853 | ||
|
|
41c61ef506 |
@@ -1,7 +1,14 @@
|
||||
---
|
||||
name: 🐛 Bug Report
|
||||
about: Report bugs or other issues to us on GitHub
|
||||
---
|
||||
|
||||
<!--
|
||||
SUPPORT REQUESTS: This is for reporting bugs in Mempool.
|
||||
If you have a support request, please join our Keybase group:
|
||||
SUPPORT REQUESTS:
|
||||
This is for reporting bugs in Mempool, not for support requests.
|
||||
If you have a support request, please join our Keybase or Matrix:
|
||||
https://keybase.io/team/mempool
|
||||
https://matrix.to/#/#mempool:bitcoin.kyoto
|
||||
-->
|
||||
|
||||
### Description
|
||||
@@ -14,11 +21,11 @@
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
<!--if you can reliably reproduce the bug, list the steps here -->
|
||||
<!-- if you can reliably reproduce the bug, list the steps here -->
|
||||
|
||||
### Expected behaviour
|
||||
|
||||
<!--description of the expected behavior -->
|
||||
<!-- description of the expected behavior -->
|
||||
|
||||
### Actual behaviour
|
||||
|
||||
@@ -26,7 +33,7 @@
|
||||
|
||||
### Screenshots
|
||||
|
||||
<!--Screenshots if gui related, drag and drop to add to the issue -->
|
||||
<!-- Screenshots if gui related, drag and drop to add to the issue -->
|
||||
|
||||
#### Device or machine
|
||||
|
||||
28
.github/ISSUE_TEMPLATE/30-feature-request.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/30-feature-request.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: ✨ Feature Request
|
||||
about: Request a feature or suggest other enhancements 💡
|
||||
---
|
||||
|
||||
<!--
|
||||
SUPPORT REQUESTS:
|
||||
This is for requesting features in Mempool, not for support requests.
|
||||
If you have a support request, please join our Keybase or Matrix:
|
||||
https://keybase.io/team/mempool
|
||||
https://matrix.to/#/#mempool:bitcoin.kyoto
|
||||
-->
|
||||
|
||||
### Description
|
||||
|
||||
<!-- brief description of the feature request -->
|
||||
|
||||
### Problem to be solved
|
||||
|
||||
<!-- description of the the problem you're having -->
|
||||
|
||||
### Proposed solution
|
||||
|
||||
<!-- explain how you think we should solve the problem -->
|
||||
|
||||
#### Additional info
|
||||
|
||||
<!-- Additional information useful for implementing (e.g. docs, links, etc.) -->
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Need help? Chat with us on Matrix
|
||||
url: https://matrix.to/#/#mempool:bitcoin.kyoto
|
||||
about: For support requests or general questions
|
||||
- name: 💬 Need help? Chat with us on Keybase
|
||||
url: https://keybase.io/team/mempool
|
||||
about: For support requests or general questions
|
||||
6
.github/workflows/cypress.yml
vendored
6
.github/workflows/cypress.yml
vendored
@@ -15,6 +15,12 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.10.0
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
- name: ${{ matrix.browser }} browser tests (Mempool)
|
||||
uses: cypress-io/github-action@v2
|
||||
with:
|
||||
|
||||
@@ -25,8 +25,7 @@ help:
|
||||
.PHONY: init
|
||||
init:
|
||||
@echo ''
|
||||
mkdir -p $(DATA) $(DATA)/mysql $(DATA)/mysql/db-scripts $(DATA)/mysql/data
|
||||
install -v mariadb-structure.sql $(DATA)/mysql/db-scripts
|
||||
mkdir -p $(DATA) $(DATA)/mysql $(DATA)/mysql/data
|
||||
#REF: https://github.com/mempool/mempool/blob/master/docker/README.md
|
||||
cat docker/docker-compose.yml > docker-compose.yml
|
||||
cat backend/mempool-config.sample.json > backend/mempool-config.json
|
||||
|
||||
275
README.md
275
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.
|
||||
|
||||

|
||||

|
||||
|
||||
## Installation Methods
|
||||
|
||||
@@ -14,17 +14,249 @@ Mempool can be self-hosted on a wide variety of your own hardware, ranging from
|
||||
4) [Production installation on a powerful FreeBSD server](https://github.com/mempool/mempool/tree/master/production)
|
||||
5) [High Availability cluster using powerful FreeBSD servers](https://github.com/mempool/mempool/tree/master/production#high-availability)
|
||||
|
||||
# Docker Installation
|
||||
|
||||
The `docker` directory contains the Dockerfiles used to build and release the official images and a `docker-compose.yml` file that is intended for end users to run a Mempool instance with minimal effort.
|
||||
|
||||
## bitcoind only configuration
|
||||
|
||||
To run an instance with the default settings, use the following command:
|
||||
|
||||
```bash
|
||||
$ docker-compose up
|
||||
```
|
||||
|
||||
The default configuration will allow you to run Mempool using `bitcoind` as the backend, so address lookups will be disabled. It assumes you have added RPC credentials for the `mempool` user with a `mempool` password in your `bitcoin.conf` file:
|
||||
|
||||
```
|
||||
rpcuser=mempool
|
||||
rpcpassword=mempool
|
||||
```
|
||||
|
||||
If you want to use your current credentials, update them in the `docker-compose.yml` file:
|
||||
|
||||
```
|
||||
api:
|
||||
environment:
|
||||
MEMPOOL_BACKEND: "none"
|
||||
RPC_HOST: "172.27.0.1"
|
||||
RPC_PORT: "8332"
|
||||
RPC_USER: "mempool"
|
||||
RPC_PASS: "mempool"
|
||||
```
|
||||
|
||||
Note: the IP in the example above refers to Docker's default gateway IP address so the container can hit the `bitcoind` instance running on the host machine. If your setup is different, update it accordingly.
|
||||
|
||||
You can check if the instance is running by visiting http://localhost - the graphs will be populated as new transactions are detected.
|
||||
|
||||
## bitcoind+electrum configuration
|
||||
|
||||
In order to run with a `electrum` compatible server as the backend, in addition to the settings required for running with `bitcoind` above, you will need to make the following changes to the `docker-compose.yml` file:
|
||||
|
||||
- Under the `api` service, change the value of the `MEMPOOL_BACKEND` key from `none` to `electrum`:
|
||||
|
||||
```
|
||||
api:
|
||||
environment:
|
||||
MEMPOOL_BACKEND: "none"
|
||||
```
|
||||
|
||||
- Under the `api` service, set the `ELECTRUM_HOST` and `ELECTRUM_PORT` keys to your Docker host IP address and set `ELECTRUM_TLS_ENABLED` to `false`:
|
||||
|
||||
```
|
||||
api:
|
||||
environment:
|
||||
ELECTRUM_HOST: "172.27.0.1"
|
||||
ELECTRUM_PORT: "50002"
|
||||
ELECTRUM_TLS_ENABLED: "false"
|
||||
```
|
||||
|
||||
You can update any of the backend settings in the `mempool-config.json` file using the following environment variables to override them under the same `api` `environment` section.
|
||||
|
||||
JSON:
|
||||
```
|
||||
"MEMPOOL": {
|
||||
"NETWORK": "mainnet",
|
||||
"BACKEND": "electrum",
|
||||
"HTTP_PORT": 8999,
|
||||
"SPAWN_CLUSTER_PROCS": 0,
|
||||
"API_URL_PREFIX": "/api/v1/",
|
||||
"POLL_RATE_MS": 2000,
|
||||
"CACHE_DIR": "./cache",
|
||||
"CLEAR_PROTECTION_MINUTES": 20,
|
||||
"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,
|
||||
"EXTERNAL_ASSETS": []
|
||||
},
|
||||
```
|
||||
|
||||
docker-compose overrides::
|
||||
```
|
||||
MEMPOOL_NETWORK: ""
|
||||
MEMPOOL_BACKEND: ""
|
||||
MEMPOOL_HTTP_PORT: ""
|
||||
MEMPOOL_SPAWN_CLUSTER_PROCS: ""
|
||||
MEMPOOL_API_URL_PREFIX: ""
|
||||
MEMPOOL_POLL_RATE_MS: ""
|
||||
MEMPOOL_CACHE_DIR: ""
|
||||
MEMPOOL_CLEAR_PROTECTION_MINUTES: ""
|
||||
MEMPOOL_RECOMMENDED_FEE_PERCENTILE: ""
|
||||
MEMPOOL_BLOCK_WEIGHT_UNITS: ""
|
||||
MEMPOOL_INITIAL_BLOCKS_AMOUNT: ""
|
||||
MEMPOOL_MEMPOOL_BLOCKS_AMOUNT: ""
|
||||
MEMPOOL_PRICE_FEED_UPDATE_INTERVAL: ""
|
||||
MEMPOOL_USE_SECOND_NODE_FOR_MINFEE: ""
|
||||
MEMPOOL_EXTERNAL_ASSETS: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"CORE_RPC": {
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 8332,
|
||||
"USERNAME": "mempool",
|
||||
"PASSWORD": "mempool"
|
||||
},
|
||||
```
|
||||
docker-compose overrides:
|
||||
```
|
||||
CORE_RPC_HOST: ""
|
||||
CORE_RPC_PORT: ""
|
||||
CORE_RPC_USERNAME: ""
|
||||
CORE_RPC_PASSWORD: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"ELECTRUM": {
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 50002,
|
||||
"TLS_ENABLED": true
|
||||
},
|
||||
```
|
||||
|
||||
docker-compose overrides:
|
||||
```
|
||||
ELECTRUM_HOST: ""
|
||||
ELECTRUM_PORT: ""
|
||||
ELECTRUM_TLS: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"ESPLORA": {
|
||||
"REST_API_URL": "http://127.0.0.1:3000"
|
||||
},
|
||||
```
|
||||
docker-compose overrides:
|
||||
```
|
||||
ESPLORA_REST_API_URL: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"SECOND_CORE_RPC": {
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 8332,
|
||||
"USERNAME": "mempool",
|
||||
"PASSWORD": "mempool"
|
||||
},
|
||||
```
|
||||
|
||||
docker-compose overrides:
|
||||
```
|
||||
SECOND_CORE_RPC_HOST: ""
|
||||
SECOND_CORE_RPC_PORT: ""
|
||||
SECOND_CORE_RPC_USERNAME: ""
|
||||
SECOND_CORE_RPC_PASSWORD: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"DATABASE": {
|
||||
"ENABLED": true,
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 3306,
|
||||
"DATABASE": "mempool",
|
||||
"USERNAME": "mempool",
|
||||
"PASSWORD": "mempool"
|
||||
},
|
||||
```
|
||||
|
||||
docker-compose overrides:
|
||||
```
|
||||
DATABASE_ENABLED: ""
|
||||
DATABASE_HOST: ""
|
||||
DATABASE_PORT: ""
|
||||
DATABASE_DATABASE: ""
|
||||
DATABASE_USERAME: ""
|
||||
DATABASE_PASSWORD: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"SYSLOG": {
|
||||
"ENABLED": true,
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 514,
|
||||
"MIN_PRIORITY": "info",
|
||||
"FACILITY": "local7"
|
||||
},
|
||||
```
|
||||
|
||||
docker-compose overrides:
|
||||
```
|
||||
SYSLOG_ENABLED: ""
|
||||
SYSLOG_HOST: ""
|
||||
SYSLOG_PORT: ""
|
||||
SYSLOG_MIN_PRIORITY: ""
|
||||
SYSLOG_FACILITY: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"STATISTICS": {
|
||||
"ENABLED": true,
|
||||
"TX_PER_SECOND_SAMPLE_PERIOD": 150
|
||||
},
|
||||
```
|
||||
|
||||
docker-compose overrides:
|
||||
```
|
||||
STATISTICS_ENABLED: ""
|
||||
STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD: ""
|
||||
```
|
||||
|
||||
JSON:
|
||||
```
|
||||
"BISQ": {
|
||||
"ENABLED": false,
|
||||
"DATA_PATH": "/bisq/statsnode-data/btc_mainnet/db"
|
||||
}
|
||||
```
|
||||
|
||||
docker-compose overrides:
|
||||
```
|
||||
BISQ_ENABLED: ""
|
||||
BISQ_DATA_PATH: ""
|
||||
```
|
||||
|
||||
|
||||
# Manual Installation
|
||||
|
||||
The following instructions are for a manual installation on Linux or FreeBSD. The file and directory paths may need to be changed to match your OS.
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Bitcoin Core (no pruning, txindex=1)
|
||||
* Electrum Server (romanz/electrs)
|
||||
* NodeJS (official stable LTS)
|
||||
* MariaDB (default config)
|
||||
* Nginx (use supplied nginx.conf and nginx-mempool.conf)
|
||||
* [Bitcoin](https://github.com/bitcoin/bitcoin)
|
||||
* [Electrum](https://github.com/romanz/electrs)
|
||||
* [NodeJS](https://github.com/nodejs/node)
|
||||
* [MariaDB](https://github.com/mariadb/server)
|
||||
* [Nginx](https://github.com/nginx/nginx)
|
||||
|
||||
## Mempool
|
||||
|
||||
@@ -41,7 +273,7 @@ Clone the mempool repo, and checkout the latest release tag:
|
||||
Enable RPC and txindex in `bitcoin.conf`:
|
||||
```bash
|
||||
rpcuser=mempool
|
||||
rpcpassword=71b61986da5b03a5694d7c7d5165ece5
|
||||
rpcpassword=mempool
|
||||
txindex=1
|
||||
```
|
||||
|
||||
@@ -54,7 +286,7 @@ Install MariaDB from OS package manager:
|
||||
|
||||
# macOS
|
||||
brew install mariadb
|
||||
brew services start mariadb
|
||||
mysql.server start
|
||||
```
|
||||
|
||||
Create database and grant privileges:
|
||||
@@ -69,18 +301,13 @@ Create database and grant privileges:
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
```
|
||||
|
||||
From the mempool repo's top-level folder, import the database structure:
|
||||
```bash
|
||||
mysql -u mempool -p mempool < mariadb-structure.sql
|
||||
```
|
||||
|
||||
## Mempool Backend
|
||||
Install mempool dependencies from npm and build the backend:
|
||||
|
||||
```bash
|
||||
# backend
|
||||
cd backend
|
||||
npm install
|
||||
npm install --prod
|
||||
npm run build
|
||||
```
|
||||
|
||||
@@ -96,18 +323,18 @@ Edit `mempool-config.json` to add your Bitcoin Core node RPC credentials:
|
||||
"MEMPOOL": {
|
||||
"NETWORK": "mainnet",
|
||||
"BACKEND": "electrum",
|
||||
"HTTP_PORT": 8999,
|
||||
"API_URL_PREFIX": "/api/v1/",
|
||||
"POLL_RATE_MS": 2000
|
||||
"HTTP_PORT": 8999
|
||||
},
|
||||
"CORE_RPC": {
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 8332,
|
||||
"USERNAME": "mempool",
|
||||
"PASSWORD": "71b61986da5b03a5694d7c7d5165ece5"
|
||||
"PASSWORD": "mempool"
|
||||
},
|
||||
"ELECTRUM": {
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": 50002,
|
||||
"TLS_ENABLED": true,
|
||||
"TLS_ENABLED": true
|
||||
},
|
||||
"DATABASE": {
|
||||
"ENABLED": true,
|
||||
@@ -116,10 +343,6 @@ Edit `mempool-config.json` to add your Bitcoin Core node RPC credentials:
|
||||
"USERNAME": "mempool",
|
||||
"PASSWORD": "mempool",
|
||||
"DATABASE": "mempool"
|
||||
},
|
||||
"STATISTICS": {
|
||||
"ENABLED": true,
|
||||
"TX_PER_SECOND_SAMPLE_PERIOD": 150
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -160,14 +383,14 @@ Install mempool dependencies from npm and build the frontend static HTML/CSS/JS:
|
||||
```bash
|
||||
# frontend
|
||||
cd frontend
|
||||
npm install
|
||||
npm install --prod
|
||||
npm run build
|
||||
```
|
||||
|
||||
Install the output into nginx webroot folder:
|
||||
|
||||
```bash
|
||||
sudo rsync -av --delete dist/mempool /var/www/
|
||||
sudo rsync -av --delete dist/ /var/www/
|
||||
```
|
||||
|
||||
## nginx + certbot
|
||||
|
||||
7
backend/.gitignore
vendored
7
backend/.gitignore
vendored
@@ -1,7 +1,10 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# production config
|
||||
mempool-config.json
|
||||
# production config and external assets
|
||||
*.json
|
||||
!mempool-config.sample.json
|
||||
|
||||
icons.json
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"INITIAL_BLOCKS_AMOUNT": 8,
|
||||
"MEMPOOL_BLOCKS_AMOUNT": 8,
|
||||
"PRICE_FEED_UPDATE_INTERVAL": 3600,
|
||||
"USE_SECOND_NODE_FOR_MINFEE": false
|
||||
"USE_SECOND_NODE_FOR_MINFEE": false,
|
||||
"EXTERNAL_ASSETS": []
|
||||
},
|
||||
"CORE_RPC": {
|
||||
"HOST": "127.0.0.1",
|
||||
|
||||
523
backend/package-lock.json
generated
523
backend/package-lock.json
generated
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"name": "mempool-backend",
|
||||
"version": "2.3.0-dev",
|
||||
"version": "2.3.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mempool-backend",
|
||||
"version": "2.3.0-dev",
|
||||
"version": "2.3.1",
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"dependencies": {
|
||||
"@mempool/bitcoin": "^3.0.3",
|
||||
"@mempool/electrum-client": "^1.1.7",
|
||||
"axios": "^0.21.1",
|
||||
"bitcoinjs-lib": "^5.2.0",
|
||||
"@types/ws": "8.2.2",
|
||||
"axios": "0.24.0",
|
||||
"bitcoinjs-lib": "6.0.1",
|
||||
"crypto-js": "^4.0.0",
|
||||
"express": "^4.17.1",
|
||||
"locutus": "^2.0.12",
|
||||
"mysql2": "2.2.5",
|
||||
"mysql2": "2.3.3",
|
||||
"node-worker-threads-pool": "^1.4.3",
|
||||
"ws": "^7.4.6"
|
||||
"typescript": "4.4.4",
|
||||
"ws": "8.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/compression": "^1.0.1",
|
||||
"@types/express": "^4.17.2",
|
||||
"@types/locutus": "^0.0.6",
|
||||
"@types/ws": "^7.4.4",
|
||||
"tslint": "^6.1.0",
|
||||
"typescript": "4.4.2"
|
||||
"tslint": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -137,8 +137,7 @@
|
||||
"node_modules/@types/node": {
|
||||
"version": "14.14.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz",
|
||||
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A=="
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.5",
|
||||
@@ -163,11 +162,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "7.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.4.tgz",
|
||||
"integrity": "sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz",
|
||||
"integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -211,11 +208,11 @@
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
@@ -225,25 +222,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/base-x": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz",
|
||||
"integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==",
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
|
||||
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/bech32": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
||||
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
|
||||
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
|
||||
},
|
||||
"node_modules/bip174": {
|
||||
"version": "2.0.1",
|
||||
@@ -253,71 +242,23 @@
|
||||
"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": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-5.2.0.tgz",
|
||||
"integrity": "sha512-5DcLxGUDejgNBYcieMIUfjORtUeNWl828VWLHJGVKZCb4zIS1oOySTUr0LGmcqJBQgTBz3bGbRQla4FgrdQEIQ==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.0.1.tgz",
|
||||
"integrity": "sha512-x/7D4jDj/MMkmO6t3p2CSDXTqpwZ/jRsRiJDmaiXabrR9XRo7jwby8HRn7EyK1h24rKFFI7vI0ay4czl6bDOZQ==",
|
||||
"dependencies": {
|
||||
"bech32": "^1.1.2",
|
||||
"bech32": "^2.0.0",
|
||||
"bip174": "^2.0.1",
|
||||
"bip32": "^2.0.4",
|
||||
"bip66": "^1.1.0",
|
||||
"bitcoin-ops": "^1.4.0",
|
||||
"bs58check": "^2.0.0",
|
||||
"bs58check": "^2.1.2",
|
||||
"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",
|
||||
"varuint-bitcoin": "^1.0.4",
|
||||
"varuint-bitcoin": "^1.1.2",
|
||||
"wif": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"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": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||
@@ -348,11 +289,6 @@
|
||||
"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": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||
@@ -487,19 +423,6 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
||||
@@ -514,9 +437,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
|
||||
"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
|
||||
"integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
@@ -548,20 +471,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||
@@ -673,9 +577,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
||||
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==",
|
||||
"version": "1.14.7",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -781,25 +685,6 @@
|
||||
"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": {
|
||||
"version": "1.7.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
@@ -980,16 +860,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": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
@@ -1026,13 +896,13 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
|
||||
"integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
|
||||
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
|
||||
"dependencies": {
|
||||
"denque": "^1.4.1",
|
||||
"denque": "^2.0.1",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.2",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^4.0.0",
|
||||
"lru-cache": "^6.0.0",
|
||||
"named-placeholders": "^1.1.2",
|
||||
@@ -1044,9 +914,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2/node_modules/iconv-lite": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
@@ -1079,11 +949,6 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||
"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": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
@@ -1135,9 +1000,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
@@ -1162,14 +1027,6 @@
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
"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": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
@@ -1178,14 +1035,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": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -1382,22 +1231,6 @@
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
@@ -1473,11 +1306,9 @@
|
||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
||||
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -1538,11 +1369,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
|
||||
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
@@ -1666,8 +1497,7 @@
|
||||
"@types/node": {
|
||||
"version": "14.14.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz",
|
||||
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A=="
|
||||
},
|
||||
"@types/qs": {
|
||||
"version": "6.9.5",
|
||||
@@ -1692,10 +1522,9 @@
|
||||
}
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "7.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.4.tgz",
|
||||
"integrity": "sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ==",
|
||||
"dev": true,
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz",
|
||||
"integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -1733,11 +1562,11 @@
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
@@ -1747,92 +1576,37 @@
|
||||
"dev": true
|
||||
},
|
||||
"base-x": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz",
|
||||
"integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==",
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
|
||||
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"bech32": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
||||
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
|
||||
},
|
||||
"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"
|
||||
}
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
|
||||
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
|
||||
},
|
||||
"bip174": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz",
|
||||
"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": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-5.2.0.tgz",
|
||||
"integrity": "sha512-5DcLxGUDejgNBYcieMIUfjORtUeNWl828VWLHJGVKZCb4zIS1oOySTUr0LGmcqJBQgTBz3bGbRQla4FgrdQEIQ==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.0.1.tgz",
|
||||
"integrity": "sha512-x/7D4jDj/MMkmO6t3p2CSDXTqpwZ/jRsRiJDmaiXabrR9XRo7jwby8HRn7EyK1h24rKFFI7vI0ay4czl6bDOZQ==",
|
||||
"requires": {
|
||||
"bech32": "^1.1.2",
|
||||
"bech32": "^2.0.0",
|
||||
"bip174": "^2.0.1",
|
||||
"bip32": "^2.0.4",
|
||||
"bip66": "^1.1.0",
|
||||
"bitcoin-ops": "^1.4.0",
|
||||
"bs58check": "^2.0.0",
|
||||
"bs58check": "^2.1.2",
|
||||
"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",
|
||||
"varuint-bitcoin": "^1.0.4",
|
||||
"varuint-bitcoin": "^1.1.2",
|
||||
"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": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||
@@ -1860,11 +1634,6 @@
|
||||
"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": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||
@@ -1983,19 +1752,6 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
||||
@@ -2010,9 +1766,9 @@
|
||||
}
|
||||
},
|
||||
"denque": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
|
||||
"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
|
||||
"integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
@@ -2035,20 +1791,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"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": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
@@ -2120,11 +1862,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||
@@ -2140,9 +1877,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
||||
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
|
||||
"version": "1.14.7",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ=="
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
@@ -2213,25 +1950,6 @@
|
||||
"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": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||
@@ -2347,11 +2065,6 @@
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
@@ -2375,16 +2088,6 @@
|
||||
"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": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
@@ -2415,13 +2118,13 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"mysql2": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
|
||||
"integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
|
||||
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
|
||||
"requires": {
|
||||
"denque": "^1.4.1",
|
||||
"denque": "^2.0.1",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.2",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^4.0.0",
|
||||
"lru-cache": "^6.0.0",
|
||||
"named-placeholders": "^1.1.2",
|
||||
@@ -2430,9 +2133,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
@@ -2463,11 +2166,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.14.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
@@ -2507,9 +2205,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
@@ -2531,27 +2229,11 @@
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
"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": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"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": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@@ -2703,18 +2385,6 @@
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
@@ -2771,10 +2441,9 @@
|
||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
||||
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
||||
"dev": true
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA=="
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
@@ -2819,9 +2488,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
|
||||
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
|
||||
"requires": {}
|
||||
},
|
||||
"yallist": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mempool-backend",
|
||||
"version": "2.3.0-dev",
|
||||
"version": "2.3.1",
|
||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"homepage": "https://mempool.space",
|
||||
@@ -30,21 +30,21 @@
|
||||
"dependencies": {
|
||||
"@mempool/bitcoin": "^3.0.3",
|
||||
"@mempool/electrum-client": "^1.1.7",
|
||||
"axios": "^0.21.1",
|
||||
"bitcoinjs-lib": "^5.2.0",
|
||||
"@types/ws": "8.2.2",
|
||||
"axios": "0.24.0",
|
||||
"bitcoinjs-lib": "6.0.1",
|
||||
"crypto-js": "^4.0.0",
|
||||
"express": "^4.17.1",
|
||||
"locutus": "^2.0.12",
|
||||
"mysql2": "2.2.5",
|
||||
"mysql2": "2.3.3",
|
||||
"node-worker-threads-pool": "^1.4.3",
|
||||
"ws": "^7.4.6"
|
||||
"typescript": "4.4.4",
|
||||
"ws": "8.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/compression": "^1.0.1",
|
||||
"@types/express": "^4.17.2",
|
||||
"@types/locutus": "^0.0.6",
|
||||
"@types/ws": "^7.4.4",
|
||||
"tslint": "^6.1.0",
|
||||
"typescript": "4.4.2"
|
||||
"tslint": "^6.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface AbstractBitcoinApi {
|
||||
$getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]>;
|
||||
$getAddressPrefix(prefix: string): string[];
|
||||
$sendRawTransaction(rawTransaction: string): Promise<string>;
|
||||
$getOutspends(txId: string): Promise<IEsploraApi.Outspend[]>;
|
||||
}
|
||||
export interface BitcoinRpcCredentials {
|
||||
host: string;
|
||||
|
||||
@@ -108,7 +108,7 @@ export namespace IBitcoinApi {
|
||||
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
|
||||
isscript: boolean; // (boolean) If the key is a script
|
||||
iswitness: boolean; // (boolean) If the address is a witness
|
||||
witness_version?: boolean; // (numeric, optional) The version number of the witness program
|
||||
witness_version?: number; // (numeric, optional) The version number 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
|
||||
|
||||
@@ -102,6 +102,18 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
return this.bitcoindClient.sendRawTransaction(rawTransaction);
|
||||
}
|
||||
|
||||
async $getOutspends(txId: string): Promise<IEsploraApi.Outspend[]> {
|
||||
const outSpends: IEsploraApi.Outspend[] = [];
|
||||
const tx = await this.$getRawTransaction(txId, true, false);
|
||||
for (let i = 0; i < tx.vout.length; i++) {
|
||||
const txOut = await this.bitcoindClient.getTxOut(txId, i);
|
||||
outSpends.push({
|
||||
spent: txOut === null,
|
||||
});
|
||||
}
|
||||
return outSpends;
|
||||
}
|
||||
|
||||
protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean): Promise<IEsploraApi.Transaction> {
|
||||
let esploraTransaction: IEsploraApi.Transaction = {
|
||||
txid: transaction.txid,
|
||||
|
||||
@@ -113,9 +113,9 @@ export namespace IEsploraApi {
|
||||
|
||||
export interface Outspend {
|
||||
spent: boolean;
|
||||
txid: string;
|
||||
vin: number;
|
||||
status: Status;
|
||||
txid?: string;
|
||||
vin?: number;
|
||||
status?: Status;
|
||||
}
|
||||
|
||||
export interface Asset {
|
||||
|
||||
@@ -60,6 +60,10 @@ class ElectrsApi implements AbstractBitcoinApi {
|
||||
$sendRawTransaction(rawTransaction: string): Promise<string> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
$getOutspends(): Promise<IEsploraApi.Outspend[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export default ElectrsApi;
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { CpfpInfo, TransactionExtended, TransactionStripped } from '../mempool.interfaces';
|
||||
import config from '../config';
|
||||
export class Common {
|
||||
static nativeAssetId = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d';
|
||||
static nativeAssetId = config.MEMPOOL.NETWORK === 'liquidtestnet' ?
|
||||
'144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49'
|
||||
: '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d';
|
||||
static _isLiquid = config.MEMPOOL.NETWORK === 'liquid' || config.MEMPOOL.NETWORK === 'liquidtestnet';
|
||||
|
||||
static isLiquid(): boolean {
|
||||
return this._isLiquid;
|
||||
}
|
||||
|
||||
static median(numbers: number[]) {
|
||||
let medianNr = 0;
|
||||
@@ -107,7 +114,7 @@ export class Common {
|
||||
totalFees += tx.bestDescendant.fee;
|
||||
}
|
||||
|
||||
tx.effectiveFeePerVsize = Math.max(config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1, totalFees / (totalWeight / 4));
|
||||
tx.effectiveFeePerVsize = Math.max(Common.isLiquid() ? 0.1 : 1, totalFees / (totalWeight / 4));
|
||||
tx.cpfpChecked = true;
|
||||
|
||||
return {
|
||||
|
||||
339
backend/src/api/database-migration.ts
Normal file
339
backend/src/api/database-migration.ts
Normal file
@@ -0,0 +1,339 @@
|
||||
import { PoolConnection } from 'mysql2/promise';
|
||||
import config from '../config';
|
||||
import { DB } from '../database';
|
||||
import logger from '../logger';
|
||||
|
||||
const sleep = (ms: number) => new Promise( res => setTimeout(res, ms));
|
||||
|
||||
class DatabaseMigration {
|
||||
private static currentVersion = 2;
|
||||
private queryTimeout = 120000;
|
||||
private statisticsAddedIndexed = false;
|
||||
|
||||
constructor() { }
|
||||
/**
|
||||
* Entry point
|
||||
*/
|
||||
public async $initializeOrMigrateDatabase(): Promise<void> {
|
||||
logger.info('MIGRATIONS: Running migrations');
|
||||
|
||||
await this.$printDatabaseVersion();
|
||||
|
||||
// First of all, if the `state` database does not exist, create it so we can track migration version
|
||||
if (!await this.$checkIfTableExists('state')) {
|
||||
logger.info('MIGRATIONS: `state` table does not exist. Creating it.');
|
||||
try {
|
||||
await this.$createMigrationStateTable();
|
||||
} catch (e) {
|
||||
logger.err('MIGRATIONS: Unable to create `state` table, aborting in 10 seconds. ' + e);
|
||||
await sleep(10000);
|
||||
process.exit(-1);
|
||||
}
|
||||
logger.info('MIGRATIONS: `state` table initialized.');
|
||||
}
|
||||
|
||||
let databaseSchemaVersion = 0;
|
||||
try {
|
||||
databaseSchemaVersion = await this.$getSchemaVersionFromDatabase();
|
||||
} catch (e) {
|
||||
logger.err('MIGRATIONS: Unable to get current database migration version, aborting in 10 seconds. ' + e);
|
||||
await sleep(10000);
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
logger.info('MIGRATIONS: Current state.schema_version ' + databaseSchemaVersion);
|
||||
logger.info('MIGRATIONS: Latest DatabaseMigration.version is ' + DatabaseMigration.currentVersion);
|
||||
if (databaseSchemaVersion >= DatabaseMigration.currentVersion) {
|
||||
logger.info('MIGRATIONS: Nothing to do.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Now, create missing tables. Those queries cannot be wrapped into a transaction unfortunately
|
||||
try {
|
||||
await this.$createMissingTablesAndIndexes(databaseSchemaVersion);
|
||||
} catch (e) {
|
||||
logger.err('MIGRATIONS: Unable to create required tables, aborting in 10 seconds. ' + e);
|
||||
await sleep(10000);
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
if (DatabaseMigration.currentVersion > databaseSchemaVersion) {
|
||||
logger.info('MIGRATIONS: Upgrading datababse schema');
|
||||
try {
|
||||
await this.$migrateTableSchemaFromVersion(databaseSchemaVersion);
|
||||
logger.info(`MIGRATIONS: OK. Database schema have been migrated from version ${databaseSchemaVersion} to ${DatabaseMigration.currentVersion} (latest version)`);
|
||||
} catch (e) {
|
||||
logger.err('MIGRATIONS: Unable to migrate database, aborting. ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all missing tables
|
||||
*/
|
||||
private async $createMissingTablesAndIndexes(databaseSchemaVersion: number) {
|
||||
await this.$setStatisticsAddedIndexedFlag(databaseSchemaVersion);
|
||||
|
||||
const connection = await DB.pool.getConnection();
|
||||
try {
|
||||
await this.$executeQuery(connection, this.getCreateElementsTableQuery(), await this.$checkIfTableExists('elements_pegs'));
|
||||
await this.$executeQuery(connection, this.getCreateStatisticsQuery(), await this.$checkIfTableExists('statistics'));
|
||||
if (databaseSchemaVersion < 2 && this.statisticsAddedIndexed === false) {
|
||||
await this.$executeQuery(connection, `CREATE INDEX added ON statistics (added);`);
|
||||
}
|
||||
connection.release();
|
||||
} catch (e) {
|
||||
connection.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case here for the `statistics` table - It appeared that somehow some dbs already had the `added` field indexed
|
||||
* while it does not appear in previous schemas. The mariadb command "CREATE INDEX IF NOT EXISTS" is not supported on
|
||||
* older mariadb version. Therefore we set a flag here in order to know if the index needs to be created or not before
|
||||
* running the migration process
|
||||
*/
|
||||
private async $setStatisticsAddedIndexedFlag(databaseSchemaVersion: number) {
|
||||
if (databaseSchemaVersion >= 2) {
|
||||
this.statisticsAddedIndexed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const connection = await DB.pool.getConnection();
|
||||
|
||||
try {
|
||||
// We don't use "CREATE INDEX IF NOT EXISTS" because it is not supported on old mariadb version 5.X
|
||||
const query = `SELECT COUNT(1) hasIndex FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE table_schema=DATABASE() AND table_name='statistics' AND index_name='added';`;
|
||||
const [rows] = await this.$executeQuery(connection, query, true);
|
||||
if (rows[0].hasIndex === 0) {
|
||||
logger.info('MIGRATIONS: `statistics.added` is not indexed');
|
||||
this.statisticsAddedIndexed = false;
|
||||
} else if (rows[0].hasIndex === 1) {
|
||||
logger.info('MIGRATIONS: `statistics.added` is already indexed');
|
||||
this.statisticsAddedIndexed = true;
|
||||
}
|
||||
} catch (e) {
|
||||
// Should really never happen but just in case it fails, we just don't execute
|
||||
// any query related to this indexing so it won't fail if the index actually already exists
|
||||
logger.err('MIGRATIONS: Unable to check if `statistics.added` INDEX exist or not.');
|
||||
this.statisticsAddedIndexed = true;
|
||||
}
|
||||
|
||||
connection.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Small query execution wrapper to log all executed queries
|
||||
*/
|
||||
private async $executeQuery(connection: PoolConnection, query: string, silent: boolean = false): Promise<any> {
|
||||
if (!silent) {
|
||||
logger.info('MIGRATIONS: Execute query:\n' + query);
|
||||
}
|
||||
return connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if 'table' exists in the database
|
||||
*/
|
||||
private async $checkIfTableExists(table: string): Promise<boolean> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${config.DATABASE.DATABASE}' AND TABLE_NAME = '${table}'`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return rows[0]['COUNT(*)'] === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current database version
|
||||
*/
|
||||
private async $getSchemaVersionFromDatabase(): Promise<number> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `SELECT number FROM state WHERE name = 'schema_version';`;
|
||||
const [rows] = await this.$executeQuery(connection, query, true);
|
||||
connection.release();
|
||||
return rows[0]['number'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the `state` table
|
||||
*/
|
||||
private async $createMigrationStateTable(): Promise<void> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
|
||||
try {
|
||||
const query = `CREATE TABLE IF NOT EXISTS state (
|
||||
name varchar(25) NOT NULL,
|
||||
number int(11) NULL,
|
||||
string varchar(100) NULL,
|
||||
CONSTRAINT name_unique UNIQUE (name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
|
||||
await this.$executeQuery(connection, query);
|
||||
|
||||
// Set initial values
|
||||
await this.$executeQuery(connection, `INSERT INTO state VALUES('schema_version', 0, NULL);`);
|
||||
await this.$executeQuery(connection, `INSERT INTO state VALUES('last_elements_block', 0, NULL);`);
|
||||
|
||||
connection.release();
|
||||
} catch (e) {
|
||||
connection.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We actually execute the migrations queries here
|
||||
*/
|
||||
private async $migrateTableSchemaFromVersion(version: number): Promise<void> {
|
||||
const transactionQueries: string[] = [];
|
||||
for (const query of this.getMigrationQueriesFromVersion(version)) {
|
||||
transactionQueries.push(query);
|
||||
}
|
||||
transactionQueries.push(this.getUpdateToLatestSchemaVersionQuery());
|
||||
|
||||
const connection = await DB.pool.getConnection();
|
||||
try {
|
||||
await this.$executeQuery(connection, 'START TRANSACTION;');
|
||||
for (const query of transactionQueries) {
|
||||
await this.$executeQuery(connection, query);
|
||||
}
|
||||
await this.$executeQuery(connection, 'COMMIT;');
|
||||
|
||||
connection.release();
|
||||
} catch (e) {
|
||||
await this.$executeQuery(connection, 'ROLLBACK;');
|
||||
connection.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate migration queries based on schema version
|
||||
*/
|
||||
private getMigrationQueriesFromVersion(version: number): string[] {
|
||||
const queries: string[] = [];
|
||||
|
||||
if (version < 1) {
|
||||
if (config.MEMPOOL.NETWORK !== 'liquid' && config.MEMPOOL.NETWORK !== 'liquidtestnet') {
|
||||
queries.push(this.getShiftStatisticsQuery());
|
||||
}
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the schema version in the database
|
||||
*/
|
||||
private getUpdateToLatestSchemaVersionQuery(): string {
|
||||
return `UPDATE state SET number = ${DatabaseMigration.currentVersion} WHERE name = 'schema_version';`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print current database version
|
||||
*/
|
||||
private async $printDatabaseVersion() {
|
||||
const connection = await DB.pool.getConnection();
|
||||
try {
|
||||
const [rows] = await this.$executeQuery(connection, 'SELECT VERSION() as version;', true);
|
||||
logger.info(`MIGRATIONS: Database engine version '${rows[0].version}'`);
|
||||
} catch (e) {
|
||||
logger.info(`MIGRATIONS: Could not fetch database engine version. ` + e);
|
||||
}
|
||||
connection.release();
|
||||
}
|
||||
|
||||
// Couple of wrappers to clean the main logic
|
||||
private getShiftStatisticsQuery(): string {
|
||||
return `UPDATE statistics SET
|
||||
vsize_1 = vsize_1 + vsize_2, vsize_2 = vsize_3,
|
||||
vsize_3 = vsize_4, vsize_4 = vsize_5,
|
||||
vsize_5 = vsize_6, vsize_6 = vsize_8,
|
||||
vsize_8 = vsize_10, vsize_10 = vsize_12,
|
||||
vsize_12 = vsize_15, vsize_15 = vsize_20,
|
||||
vsize_20 = vsize_30, vsize_30 = vsize_40,
|
||||
vsize_40 = vsize_50, vsize_50 = vsize_60,
|
||||
vsize_60 = vsize_70, vsize_70 = vsize_80,
|
||||
vsize_80 = vsize_90, vsize_90 = vsize_100,
|
||||
vsize_100 = vsize_125, vsize_125 = vsize_150,
|
||||
vsize_150 = vsize_175, vsize_175 = vsize_200,
|
||||
vsize_200 = vsize_250, vsize_250 = vsize_300,
|
||||
vsize_300 = vsize_350, vsize_350 = vsize_400,
|
||||
vsize_400 = vsize_500, vsize_500 = vsize_600,
|
||||
vsize_600 = vsize_700, vsize_700 = vsize_800,
|
||||
vsize_800 = vsize_900, vsize_900 = vsize_1000,
|
||||
vsize_1000 = vsize_1200, vsize_1200 = vsize_1400,
|
||||
vsize_1400 = vsize_1800, vsize_1800 = vsize_2000, vsize_2000 = 0;`;
|
||||
}
|
||||
|
||||
private getCreateStatisticsQuery(): string {
|
||||
return `CREATE TABLE IF NOT EXISTS statistics (
|
||||
id int(11) NOT NULL AUTO_INCREMENT,
|
||||
added datetime NOT NULL,
|
||||
unconfirmed_transactions int(11) UNSIGNED NOT NULL,
|
||||
tx_per_second float UNSIGNED NOT NULL,
|
||||
vbytes_per_second int(10) UNSIGNED NOT NULL,
|
||||
mempool_byte_weight int(10) UNSIGNED NOT NULL,
|
||||
fee_data longtext NOT NULL,
|
||||
total_fee double UNSIGNED NOT NULL,
|
||||
vsize_1 int(11) NOT NULL,
|
||||
vsize_2 int(11) NOT NULL,
|
||||
vsize_3 int(11) NOT NULL,
|
||||
vsize_4 int(11) NOT NULL,
|
||||
vsize_5 int(11) NOT NULL,
|
||||
vsize_6 int(11) NOT NULL,
|
||||
vsize_8 int(11) NOT NULL,
|
||||
vsize_10 int(11) NOT NULL,
|
||||
vsize_12 int(11) NOT NULL,
|
||||
vsize_15 int(11) NOT NULL,
|
||||
vsize_20 int(11) NOT NULL,
|
||||
vsize_30 int(11) NOT NULL,
|
||||
vsize_40 int(11) NOT NULL,
|
||||
vsize_50 int(11) NOT NULL,
|
||||
vsize_60 int(11) NOT NULL,
|
||||
vsize_70 int(11) NOT NULL,
|
||||
vsize_80 int(11) NOT NULL,
|
||||
vsize_90 int(11) NOT NULL,
|
||||
vsize_100 int(11) NOT NULL,
|
||||
vsize_125 int(11) NOT NULL,
|
||||
vsize_150 int(11) NOT NULL,
|
||||
vsize_175 int(11) NOT NULL,
|
||||
vsize_200 int(11) NOT NULL,
|
||||
vsize_250 int(11) NOT NULL,
|
||||
vsize_300 int(11) NOT NULL,
|
||||
vsize_350 int(11) NOT NULL,
|
||||
vsize_400 int(11) NOT NULL,
|
||||
vsize_500 int(11) NOT NULL,
|
||||
vsize_600 int(11) NOT NULL,
|
||||
vsize_700 int(11) NOT NULL,
|
||||
vsize_800 int(11) NOT NULL,
|
||||
vsize_900 int(11) NOT NULL,
|
||||
vsize_1000 int(11) NOT NULL,
|
||||
vsize_1200 int(11) NOT NULL,
|
||||
vsize_1400 int(11) NOT NULL,
|
||||
vsize_1600 int(11) NOT NULL,
|
||||
vsize_1800 int(11) NOT NULL,
|
||||
vsize_2000 int(11) NOT NULL,
|
||||
CONSTRAINT PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
|
||||
}
|
||||
|
||||
private getCreateElementsTableQuery(): string {
|
||||
return `CREATE TABLE IF NOT EXISTS elements_pegs (
|
||||
block int(11) NOT NULL,
|
||||
datetime int(11) NOT NULL,
|
||||
amount bigint(20) NOT NULL,
|
||||
txid varchar(65) NOT NULL,
|
||||
txindex int(11) NOT NULL,
|
||||
bitcoinaddress varchar(100) NOT NULL,
|
||||
bitcointxid varchar(65) NOT NULL,
|
||||
bitcoinindex int(11) NOT NULL,
|
||||
final_tx int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
|
||||
}
|
||||
}
|
||||
|
||||
export default new DatabaseMigration();
|
||||
@@ -1,12 +1,13 @@
|
||||
import config from '../config';
|
||||
import { MempoolBlock } from '../mempool.interfaces';
|
||||
import { Common } from './common';
|
||||
import mempool from './mempool';
|
||||
import projectedBlocks from './mempool-blocks';
|
||||
|
||||
class FeeApi {
|
||||
constructor() { }
|
||||
|
||||
defaultFee = config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1;
|
||||
defaultFee = Common.isLiquid() ? 0.1 : 1;
|
||||
|
||||
public getRecommendedFee() {
|
||||
const pBlocks = projectedBlocks.getMempoolBlocks();
|
||||
|
||||
@@ -18,12 +18,12 @@ class ElementsParser {
|
||||
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 latestBlockHeight = await this.$getLatestBlockHeightFromDatabase();
|
||||
for (let height = latestBlockHeight + 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);
|
||||
await this.$saveLatestBlockToDatabase(block.height);
|
||||
}
|
||||
this.isRunning = false;
|
||||
} catch (e) {
|
||||
@@ -92,18 +92,18 @@ class ElementsParser {
|
||||
logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`);
|
||||
}
|
||||
|
||||
protected async $getLatestBlockFromDatabase(): Promise<any> {
|
||||
protected async $getLatestBlockHeightFromDatabase(): Promise<number> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `SELECT block, datetime, block_hash FROM last_elements_block`;
|
||||
const query = `SELECT number FROM state WHERE name = 'last_elements_block'`;
|
||||
const [rows] = await connection.query<any>(query);
|
||||
connection.release();
|
||||
return rows[0];
|
||||
return rows[0]['number'];
|
||||
}
|
||||
|
||||
protected async $saveLatestBlockToDatabase(blockHeight: number, datetime: number, blockHash: string) {
|
||||
protected async $saveLatestBlockToDatabase(blockHeight: number) {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `UPDATE last_elements_block SET block = ?, datetime = ?, block_hash = ?`;
|
||||
await connection.query<any>(query, [blockHeight, datetime, blockHash]);
|
||||
const query = `UPDATE state SET number = ? WHERE name = 'last_elements_block'`;
|
||||
await connection.query<any>(query, [blockHeight]);
|
||||
connection.release();
|
||||
}
|
||||
}
|
||||
|
||||
39
backend/src/api/liquid/icons.ts
Normal file
39
backend/src/api/liquid/icons.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import * as fs from 'fs';
|
||||
import config from '../../config';
|
||||
import logger from '../../logger';
|
||||
|
||||
class Icons {
|
||||
private static FILE_NAME = './icons.json';
|
||||
private iconIds: string[] = [];
|
||||
private icons: { [assetId: string]: string; } = {};
|
||||
|
||||
constructor() {}
|
||||
|
||||
public loadIcons() {
|
||||
if (!fs.existsSync(Icons.FILE_NAME)) {
|
||||
logger.warn(`${Icons.FILE_NAME} does not exist. No Liquid icons loaded.`);
|
||||
return;
|
||||
}
|
||||
const cacheData = fs.readFileSync(Icons.FILE_NAME, 'utf8');
|
||||
this.icons = JSON.parse(cacheData);
|
||||
|
||||
for (const i in this.icons) {
|
||||
this.iconIds.push(i);
|
||||
}
|
||||
logger.debug(`Liquid icons has been loaded.`);
|
||||
}
|
||||
|
||||
public getIconByAssetId(assetId: string): Buffer | undefined {
|
||||
const icon = this.icons[assetId];
|
||||
if (icon) {
|
||||
return Buffer.from(icon, 'base64');
|
||||
}
|
||||
}
|
||||
|
||||
public getAllIconIds() {
|
||||
return this.iconIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new Icons();
|
||||
@@ -4,14 +4,12 @@ import logger from '../logger';
|
||||
|
||||
import { Statistic, TransactionExtended, OptimizedStatistic } from '../mempool.interfaces';
|
||||
import config from '../config';
|
||||
import { Common } from './common';
|
||||
|
||||
class Statistics {
|
||||
protected intervalTimer: NodeJS.Timer | undefined;
|
||||
protected newStatisticsEntryCallback: ((stats: OptimizedStatistic) => void) | undefined;
|
||||
protected queryTimeout = 120000;
|
||||
protected cache: { [date: string]: OptimizedStatistic[] } = {
|
||||
'24h': [], '1w': [], '1m': [], '3m': [], '6m': [], '1y': [], '2y': [], '3y': []
|
||||
};
|
||||
|
||||
public setNewStatisticsEntryCallback(fn: (stats: OptimizedStatistic) => void) {
|
||||
this.newStatisticsEntryCallback = fn;
|
||||
@@ -33,25 +31,6 @@ class Statistics {
|
||||
this.runStatistics();
|
||||
}, 1 * 60 * 1000);
|
||||
}, difference);
|
||||
|
||||
this.createCache();
|
||||
setInterval(this.createCache.bind(this), 600000);
|
||||
}
|
||||
|
||||
public getCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
private async createCache() {
|
||||
this.cache['24h'] = await this.$list24H();
|
||||
this.cache['1w'] = await this.$list1W();
|
||||
this.cache['1m'] = await this.$list1M();
|
||||
this.cache['3m'] = await this.$list3M();
|
||||
this.cache['6m'] = await this.$list6M();
|
||||
this.cache['1y'] = await this.$list1Y();
|
||||
this.cache['2y'] = await this.$list2Y();
|
||||
this.cache['3y'] = await this.$list3Y();
|
||||
logger.debug('Statistics cache created');
|
||||
}
|
||||
|
||||
private async runStatistics(): Promise<void> {
|
||||
@@ -90,9 +69,9 @@ class Statistics {
|
||||
memPoolArray.forEach((transaction) => {
|
||||
for (let i = 0; i < logFees.length; i++) {
|
||||
if (
|
||||
(config.MEMPOOL.NETWORK === 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize * 10 < logFees[i + 1]))
|
||||
(Common.isLiquid() && (i === lastItem || transaction.effectiveFeePerVsize * 10 < logFees[i + 1]))
|
||||
||
|
||||
(config.MEMPOOL.NETWORK !== 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize < logFees[i + 1]))
|
||||
(!Common.isLiquid() && (i === lastItem || transaction.effectiveFeePerVsize < logFees[i + 1]))
|
||||
) {
|
||||
if (weightVsizeFees[logFees[i]]) {
|
||||
weightVsizeFees[logFees[i]] += transaction.vsize;
|
||||
@@ -267,10 +246,58 @@ class Statistics {
|
||||
}
|
||||
}
|
||||
|
||||
private getQueryForDays(div: number) {
|
||||
return `SELECT id, added, unconfirmed_transactions,
|
||||
tx_per_second,
|
||||
vbytes_per_second,
|
||||
private getQueryForDaysAvg(div: number, interval: string) {
|
||||
return `SELECT
|
||||
UNIX_TIMESTAMP(added) as added,
|
||||
CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second,
|
||||
CAST(avg(vsize_1) as DOUBLE) as vsize_1,
|
||||
CAST(avg(vsize_2) as DOUBLE) as vsize_2,
|
||||
CAST(avg(vsize_3) as DOUBLE) as vsize_3,
|
||||
CAST(avg(vsize_4) as DOUBLE) as vsize_4,
|
||||
CAST(avg(vsize_5) as DOUBLE) as vsize_5,
|
||||
CAST(avg(vsize_6) as DOUBLE) as vsize_6,
|
||||
CAST(avg(vsize_8) as DOUBLE) as vsize_8,
|
||||
CAST(avg(vsize_10) as DOUBLE) as vsize_10,
|
||||
CAST(avg(vsize_12) as DOUBLE) as vsize_12,
|
||||
CAST(avg(vsize_15) as DOUBLE) as vsize_15,
|
||||
CAST(avg(vsize_20) as DOUBLE) as vsize_20,
|
||||
CAST(avg(vsize_30) as DOUBLE) as vsize_30,
|
||||
CAST(avg(vsize_40) as DOUBLE) as vsize_40,
|
||||
CAST(avg(vsize_50) as DOUBLE) as vsize_50,
|
||||
CAST(avg(vsize_60) as DOUBLE) as vsize_60,
|
||||
CAST(avg(vsize_70) as DOUBLE) as vsize_70,
|
||||
CAST(avg(vsize_80) as DOUBLE) as vsize_80,
|
||||
CAST(avg(vsize_90) as DOUBLE) as vsize_90,
|
||||
CAST(avg(vsize_100) as DOUBLE) as vsize_100,
|
||||
CAST(avg(vsize_125) as DOUBLE) as vsize_125,
|
||||
CAST(avg(vsize_150) as DOUBLE) as vsize_150,
|
||||
CAST(avg(vsize_175) as DOUBLE) as vsize_175,
|
||||
CAST(avg(vsize_200) as DOUBLE) as vsize_200,
|
||||
CAST(avg(vsize_250) as DOUBLE) as vsize_250,
|
||||
CAST(avg(vsize_300) as DOUBLE) as vsize_300,
|
||||
CAST(avg(vsize_350) as DOUBLE) as vsize_350,
|
||||
CAST(avg(vsize_400) as DOUBLE) as vsize_400,
|
||||
CAST(avg(vsize_500) as DOUBLE) as vsize_500,
|
||||
CAST(avg(vsize_600) as DOUBLE) as vsize_600,
|
||||
CAST(avg(vsize_700) as DOUBLE) as vsize_700,
|
||||
CAST(avg(vsize_800) as DOUBLE) as vsize_800,
|
||||
CAST(avg(vsize_900) as DOUBLE) as vsize_900,
|
||||
CAST(avg(vsize_1000) as DOUBLE) as vsize_1000,
|
||||
CAST(avg(vsize_1200) as DOUBLE) as vsize_1200,
|
||||
CAST(avg(vsize_1400) as DOUBLE) as vsize_1400,
|
||||
CAST(avg(vsize_1600) as DOUBLE) as vsize_1600,
|
||||
CAST(avg(vsize_1800) as DOUBLE) as vsize_1800,
|
||||
CAST(avg(vsize_2000) as DOUBLE) as vsize_2000 \
|
||||
FROM statistics \
|
||||
WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() \
|
||||
GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \
|
||||
ORDER BY statistics.added DESC;`;
|
||||
}
|
||||
|
||||
private getQueryForDays(div: number, interval: string) {
|
||||
return `SELECT
|
||||
UNIX_TIMESTAMP(added) as added,
|
||||
CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second,
|
||||
vsize_1,
|
||||
vsize_2,
|
||||
vsize_3,
|
||||
@@ -308,13 +335,17 @@ class Statistics {
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000 FROM statistics GROUP BY UNIX_TIMESTAMP(added) DIV ${div} ORDER BY id DESC LIMIT 480`;
|
||||
vsize_2000 \
|
||||
FROM statistics \
|
||||
WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() \
|
||||
GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \
|
||||
ORDER BY statistics.added DESC;`;
|
||||
}
|
||||
|
||||
public async $get(id: number): Promise<OptimizedStatistic | undefined> {
|
||||
private async $get(id: number): Promise<OptimizedStatistic | undefined> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `SELECT * FROM statistics WHERE id = ?`;
|
||||
const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE id = ?`;
|
||||
const [rows] = await connection.query<any>(query, [id]);
|
||||
connection.release();
|
||||
if (rows[0]) {
|
||||
@@ -328,7 +359,7 @@ class Statistics {
|
||||
public async $list2H(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `SELECT * FROM statistics ORDER BY id DESC LIMIT 120`;
|
||||
const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 120`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -341,7 +372,7 @@ class Statistics {
|
||||
public async $list24H(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(180);
|
||||
const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY statistics.added DESC LIMIT 1440`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -354,7 +385,7 @@ class Statistics {
|
||||
public async $list1W(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(1260);
|
||||
const query = this.getQueryForDaysAvg(300, '1 WEEK'); // 5m interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -367,7 +398,7 @@ class Statistics {
|
||||
public async $list1M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(5040);
|
||||
const query = this.getQueryForDaysAvg(1800, '1 MONTH'); // 30m interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -380,7 +411,7 @@ class Statistics {
|
||||
public async $list3M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(15120);
|
||||
const query = this.getQueryForDaysAvg(7200, '3 MONTH'); // 2h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -393,7 +424,7 @@ class Statistics {
|
||||
public async $list6M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(30240);
|
||||
const query = this.getQueryForDaysAvg(10800, '6 MONTH'); // 3h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -406,7 +437,7 @@ class Statistics {
|
||||
public async $list1Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(60480);
|
||||
const query = this.getQueryForDays(28800, '1 YEAR'); // 8h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -419,7 +450,7 @@ class Statistics {
|
||||
public async $list2Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(120960);
|
||||
const query = this.getQueryForDays(28800, "2 YEAR"); // 8h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -432,7 +463,7 @@ class Statistics {
|
||||
public async $list3Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(181440);
|
||||
const query = this.getQueryForDays(43200, "3 YEAR"); // 12h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -445,10 +476,7 @@ class Statistics {
|
||||
private mapStatisticToOptimizedStatistic(statistic: Statistic[]): OptimizedStatistic[] {
|
||||
return statistic.map((s) => {
|
||||
return {
|
||||
id: s.id || 0,
|
||||
added: s.added,
|
||||
unconfirmed_transactions: s.unconfirmed_transactions,
|
||||
tx_per_second: s.tx_per_second,
|
||||
vbytes_per_second: s.vbytes_per_second,
|
||||
mempool_byte_weight: s.mempool_byte_weight,
|
||||
total_fee: s.total_fee,
|
||||
|
||||
@@ -2,6 +2,7 @@ import bitcoinApi from './bitcoin/bitcoin-api-factory';
|
||||
import { TransactionExtended, TransactionMinerInfo } from '../mempool.interfaces';
|
||||
import { IEsploraApi } from './bitcoin/esplora-api.interface';
|
||||
import config from '../config';
|
||||
import { Common } from './common';
|
||||
|
||||
class TransactionUtils {
|
||||
constructor() { }
|
||||
@@ -31,7 +32,8 @@ class TransactionUtils {
|
||||
// @ts-ignore
|
||||
return transaction;
|
||||
}
|
||||
const feePerVbytes = Math.max(config.MEMPOOL.NETWORK === 'liquid' ? 0.1 : 1, (transaction.fee || 0) / (transaction.weight / 4));
|
||||
const feePerVbytes = Math.max(Common.isLiquid() ? 0.1 : 1,
|
||||
(transaction.fee || 0) / (transaction.weight / 4));
|
||||
const transactionExtended: TransactionExtended = Object.assign({
|
||||
vsize: Math.round(transaction.weight / 4),
|
||||
feePerVsize: feePerVbytes,
|
||||
|
||||
@@ -2,7 +2,7 @@ const configFile = require('../mempool-config.json');
|
||||
|
||||
interface IConfig {
|
||||
MEMPOOL: {
|
||||
NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid';
|
||||
NETWORK: 'mainnet' | 'testnet' | 'signet' | 'liquid' | 'liquidtestnet';
|
||||
BACKEND: 'esplora' | 'electrum' | 'none';
|
||||
HTTP_PORT: number;
|
||||
SPAWN_CLUSTER_PROCS: number;
|
||||
@@ -16,6 +16,7 @@ interface IConfig {
|
||||
MEMPOOL_BLOCKS_AMOUNT: number;
|
||||
PRICE_FEED_UPDATE_INTERVAL: number;
|
||||
USE_SECOND_NODE_FOR_MINFEE: boolean;
|
||||
EXTERNAL_ASSETS: string[];
|
||||
};
|
||||
ESPLORA: {
|
||||
REST_API_URL: string;
|
||||
@@ -78,6 +79,7 @@ const defaults: IConfig = {
|
||||
'MEMPOOL_BLOCKS_AMOUNT': 8,
|
||||
'PRICE_FEED_UPDATE_INTERVAL': 3600,
|
||||
'USE_SECOND_NODE_FOR_MINFEE': false,
|
||||
'EXTERNAL_ASSETS': [],
|
||||
},
|
||||
'ESPLORA': {
|
||||
'REST_API_URL': 'http://127.0.0.1:3000',
|
||||
|
||||
@@ -21,6 +21,10 @@ import backendInfo from './api/backend-info';
|
||||
import loadingIndicators from './api/loading-indicators';
|
||||
import mempool from './api/mempool';
|
||||
import elementsParser from './api/liquid/elements-parser';
|
||||
import databaseMigration from './api/database-migration';
|
||||
import syncAssets from './sync-assets';
|
||||
import icons from './api/liquid/icons';
|
||||
import { Common } from './api/common';
|
||||
|
||||
class Server {
|
||||
private wss: WebSocket.Server | undefined;
|
||||
@@ -77,16 +81,26 @@ class Server {
|
||||
|
||||
this.setUpWebsocketHandling();
|
||||
|
||||
await syncAssets.syncAssets();
|
||||
diskCache.loadMempoolCache();
|
||||
|
||||
if (config.DATABASE.ENABLED) {
|
||||
await checkDbConnection();
|
||||
try {
|
||||
await databaseMigration.$initializeOrMigrateDatabase();
|
||||
} catch (e) {
|
||||
throw new Error(e instanceof Error ? e.message : 'Error');
|
||||
}
|
||||
}
|
||||
|
||||
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && cluster.isMaster) {
|
||||
statistics.startStatistics();
|
||||
}
|
||||
|
||||
if (Common.isLiquid()) {
|
||||
icons.loadIcons();
|
||||
}
|
||||
|
||||
fiatConversion.startService();
|
||||
|
||||
this.setUpHttpApiRoutes();
|
||||
@@ -143,7 +157,7 @@ class Server {
|
||||
if (this.wss) {
|
||||
websocketHandler.setWebsocketServer(this.wss);
|
||||
}
|
||||
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||
if (Common.isLiquid() && config.DATABASE.ENABLED) {
|
||||
blocks.setNewBlockCallback(async () => {
|
||||
try {
|
||||
await elementsParser.$parse();
|
||||
@@ -207,19 +221,37 @@ class Server {
|
||||
res.status(500).end();
|
||||
}
|
||||
})
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'translators', async (req, res) => {
|
||||
try {
|
||||
const response = await axios.get('https://mempool.space/api/v1/translators', { responseType: 'stream', timeout: 10000 });
|
||||
response.data.pipe(res);
|
||||
} catch (e) {
|
||||
res.status(500).end();
|
||||
}
|
||||
})
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'translators/images/:id', async (req, res) => {
|
||||
try {
|
||||
const response = await axios.get('https://mempool.space/api/v1/translators/images/' + req.params.id, {
|
||||
responseType: 'stream', timeout: 10000
|
||||
});
|
||||
response.data.pipe(res);
|
||||
} catch (e) {
|
||||
res.status(500).end();
|
||||
}
|
||||
})
|
||||
;
|
||||
|
||||
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/2h', routes.get2HStatistics)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/24h', routes.get24HStatistics.bind(routes))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1w', routes.get1WHStatistics.bind(routes))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1m', routes.get1MStatistics.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/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))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/2h', routes.$getStatisticsByTime.bind(routes, '2h'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/24h', routes.$getStatisticsByTime.bind(routes, '24h'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1w', routes.$getStatisticsByTime.bind(routes, '1w'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1m', routes.$getStatisticsByTime.bind(routes, '1m'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/3m', routes.$getStatisticsByTime.bind(routes, '3m'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/6m', routes.$getStatisticsByTime.bind(routes, '6m'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1y', routes.$getStatisticsByTime.bind(routes, '1y'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/2y', routes.$getStatisticsByTime.bind(routes, '2y'))
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/3y', routes.$getStatisticsByTime.bind(routes, '3y'))
|
||||
;
|
||||
}
|
||||
|
||||
@@ -270,7 +302,14 @@ class Server {
|
||||
;
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||
if (Common.isLiquid()) {
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'assets/icons', routes.getAllLiquidIcon)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'asset/:assetId/icon', routes.getLiquidIcon)
|
||||
;
|
||||
}
|
||||
|
||||
if (Common.isLiquid() && config.DATABASE.ENABLED) {
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', routes.$getElementsPegsByMonth)
|
||||
;
|
||||
|
||||
@@ -128,10 +128,7 @@ export interface Statistic {
|
||||
}
|
||||
|
||||
export interface OptimizedStatistic {
|
||||
id: number;
|
||||
added: string;
|
||||
unconfirmed_transactions: number;
|
||||
tx_per_second: number;
|
||||
vbytes_per_second: number;
|
||||
total_fee: number;
|
||||
mempool_byte_weight: number;
|
||||
|
||||
@@ -19,45 +19,55 @@ import loadingIndicators from './api/loading-indicators';
|
||||
import { Common } from './api/common';
|
||||
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
||||
import elementsParser from './api/liquid/elements-parser';
|
||||
import icons from './api/liquid/icons';
|
||||
|
||||
class Routes {
|
||||
constructor() {}
|
||||
|
||||
public async get2HStatistics(req: Request, res: Response) {
|
||||
const result = await statistics.$list2H();
|
||||
res.json(result);
|
||||
}
|
||||
public async $getStatisticsByTime(time: '2h' | '24h' | '1w' | '1m' | '3m' | '6m' | '1y' | '2y' | '3y', req: Request, res: Response) {
|
||||
res.header('Pragma', 'public');
|
||||
res.header('Cache-control', 'public');
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||
|
||||
public get24HStatistics(req: Request, res: Response) {
|
||||
res.json(statistics.getCache()['24h']);
|
||||
}
|
||||
|
||||
public get1WHStatistics(req: Request, res: Response) {
|
||||
res.json(statistics.getCache()['1w']);
|
||||
}
|
||||
|
||||
public get1MStatistics(req: Request, res: Response) {
|
||||
res.json(statistics.getCache()['1m']);
|
||||
}
|
||||
|
||||
public get3MStatistics(req: Request, res: Response) {
|
||||
res.json(statistics.getCache()['3m']);
|
||||
}
|
||||
|
||||
public get6MStatistics(req: Request, res: Response) {
|
||||
res.json(statistics.getCache()['6m']);
|
||||
}
|
||||
|
||||
public get1YStatistics(req: Request, res: Response) {
|
||||
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']);
|
||||
try {
|
||||
let result;
|
||||
switch (time as string) {
|
||||
case '2h':
|
||||
result = await statistics.$list2H();
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||
break;
|
||||
case '24h':
|
||||
result = await statistics.$list24H();
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||
break;
|
||||
case '1w':
|
||||
result = await statistics.$list1W();
|
||||
break;
|
||||
case '1m':
|
||||
result = await statistics.$list1M();
|
||||
break;
|
||||
case '3m':
|
||||
result = await statistics.$list3M();
|
||||
break;
|
||||
case '6m':
|
||||
result = await statistics.$list6M();
|
||||
break;
|
||||
case '1y':
|
||||
result = await statistics.$list1Y();
|
||||
break;
|
||||
case '2y':
|
||||
result = await statistics.$list2Y();
|
||||
break;
|
||||
case '3y':
|
||||
result = await statistics.$list3Y();
|
||||
break;
|
||||
default:
|
||||
result = await statistics.$list2H();
|
||||
}
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
public getInitData(req: Request, res: Response) {
|
||||
@@ -69,7 +79,7 @@ class Routes {
|
||||
}
|
||||
}
|
||||
|
||||
public async getRecommendedFees(req: Request, res: Response) {
|
||||
public getRecommendedFees(req: Request, res: Response) {
|
||||
if (!mempool.isInSync()) {
|
||||
res.statusCode = 503;
|
||||
res.send('Service Unavailable');
|
||||
@@ -706,8 +716,13 @@ class Routes {
|
||||
}
|
||||
}
|
||||
|
||||
public getTransactionOutspends(req: Request, res: Response) {
|
||||
res.status(501).send('Not implemented');
|
||||
public async getTransactionOutspends(req: Request, res: Response) {
|
||||
try {
|
||||
const result = await bitcoinApi.$getOutspends(req.params.txId);
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
public getDifficultyChange(req: Request, res: Response) {
|
||||
@@ -807,6 +822,26 @@ class Routes {
|
||||
: (e.message || 'Error'));
|
||||
}
|
||||
}
|
||||
|
||||
public getLiquidIcon(req: Request, res: Response) {
|
||||
const result = icons.getIconByAssetId(req.params.assetId);
|
||||
if (result) {
|
||||
res.setHeader('content-type', 'image/png');
|
||||
res.setHeader('content-length', result.length);
|
||||
res.send(result);
|
||||
} else {
|
||||
res.status(404).send('Asset icon not found');
|
||||
}
|
||||
}
|
||||
|
||||
public getAllLiquidIcon(req: Request, res: Response) {
|
||||
const result = icons.getAllIconIds();
|
||||
if (result) {
|
||||
res.json(result);
|
||||
} else {
|
||||
res.status(404).send('Asset icons not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Routes();
|
||||
|
||||
32
backend/src/sync-assets.ts
Normal file
32
backend/src/sync-assets.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import axios from 'axios';
|
||||
import * as fs from 'fs';
|
||||
const fsPromises = fs.promises;
|
||||
import config from './config';
|
||||
import logger from './logger';
|
||||
|
||||
const PATH = './';
|
||||
|
||||
class SyncAssets {
|
||||
constructor() { }
|
||||
|
||||
public async syncAssets() {
|
||||
for (const url of config.MEMPOOL.EXTERNAL_ASSETS) {
|
||||
await this.downloadFile(url);
|
||||
}
|
||||
}
|
||||
|
||||
private async downloadFile(url: string) {
|
||||
const fileName = url.split('/').slice(-1)[0];
|
||||
logger.info(`Downloading external asset: ${fileName}...`);
|
||||
try {
|
||||
const response = await axios.get(url, {
|
||||
responseType: 'stream', timeout: 30000
|
||||
});
|
||||
await fsPromises.writeFile(PATH + fileName, response.data);
|
||||
} catch (e: any) {
|
||||
throw new Error(`Failed to download external asset. ` + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new SyncAssets();
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"lib": ["es2019"],
|
||||
"lib": ["es2019", "dom"],
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false,
|
||||
|
||||
101
docker/README.md
101
docker/README.md
@@ -1,101 +0,0 @@
|
||||
# Docker
|
||||
|
||||
## Initialization
|
||||
|
||||
In an empty dir create 2 sub-dirs
|
||||
|
||||
```bash
|
||||
mkdir -p data mysql/data mysql/db-scripts
|
||||
```
|
||||
|
||||
In the `mysql/db-scripts` sub-dir add the `mariadb-structure.sql` file from the mempool repo
|
||||
|
||||
Your dir should now look like that:
|
||||
|
||||
```bash
|
||||
$ls -R
|
||||
.:
|
||||
data mysql
|
||||
|
||||
./data:
|
||||
|
||||
./mysql:
|
||||
data db-scripts
|
||||
|
||||
./mysql/data:
|
||||
|
||||
./mysql/db-scripts:
|
||||
mariadb-structure.sql
|
||||
```
|
||||
|
||||
In the main dir add the following `docker-compose.yml`
|
||||
|
||||
```bash
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
web:
|
||||
image: mempool/frontend:latest
|
||||
user: "1000:1000"
|
||||
restart: on-failure
|
||||
stop_grace_period: 1m
|
||||
command: "./wait-for db:3306 --timeout=720 -- nginx -g 'daemon off;'"
|
||||
ports:
|
||||
- 80:8080
|
||||
environment:
|
||||
FRONTEND_HTTP_PORT: "8080"
|
||||
BACKEND_MAINNET_HTTP_HOST: "api"
|
||||
api:
|
||||
image: mempool/backend:latest
|
||||
user: "1000:1000"
|
||||
restart: on-failure
|
||||
stop_grace_period: 1m
|
||||
command: "./wait-for-it.sh db:3306 --timeout=720 --strict -- ./start.sh"
|
||||
volumes:
|
||||
- ./data:/backend/cache
|
||||
environment:
|
||||
RPC_HOST: "127.0.0.1"
|
||||
RPC_PORT: "8332"
|
||||
RPC_USER: "mempool"
|
||||
RPC_PASS: "mempool"
|
||||
ELECTRUM_HOST: "127.0.0.1"
|
||||
ELECTRUM_PORT: "50002"
|
||||
ELECTRUM_TLS: "false"
|
||||
MYSQL_HOST: "db"
|
||||
MYSQL_PORT: "3306"
|
||||
MYSQL_DATABASE: "mempool"
|
||||
MYSQL_USER: "mempool"
|
||||
MYSQL_PASS: "mempool"
|
||||
BACKEND_MAINNET_HTTP_PORT: "8999"
|
||||
CACHE_DIR: "/backend/cache"
|
||||
MEMPOOL_CLEAR_PROTECTION_MINUTES: "20"
|
||||
db:
|
||||
image: mariadb:10.5.8
|
||||
user: "1000:1000"
|
||||
restart: on-failure
|
||||
stop_grace_period: 1m
|
||||
volumes:
|
||||
- ./mysql/data:/var/lib/mysql
|
||||
- ./mysql/db-scripts:/docker-entrypoint-initdb.d
|
||||
environment:
|
||||
MYSQL_DATABASE: "mempool"
|
||||
MYSQL_USER: "mempool"
|
||||
MYSQL_PASSWORD: "mempool"
|
||||
MYSQL_ROOT_PASSWORD: "admin"
|
||||
|
||||
```
|
||||
|
||||
You can update all the environment variables inside the API container, especially the RPC and ELECTRUM ones
|
||||
|
||||
## Run it
|
||||
|
||||
To run our docker-compose use the following cmd:
|
||||
|
||||
```bash
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
If everything went okay you should see the beautiful mempool :grin:
|
||||
|
||||
If you get stuck on "loading blocks", this means the websocket can't connect.
|
||||
Check your nginx proxy setup, firewalls, etc. and open an issue if you need help.
|
||||
@@ -1,38 +1,62 @@
|
||||
{
|
||||
"MEMPOOL": {
|
||||
"NETWORK": "mainnet",
|
||||
"BACKEND": "electrum",
|
||||
"HTTP_PORT": __MEMPOOL_BACKEND_MAINNET_HTTP_PORT__,
|
||||
"SPAWN_CLUSTER_PROCS": 0,
|
||||
"API_URL_PREFIX": "/api/v1/",
|
||||
"POLL_RATE_MS": 2000,
|
||||
"CACHE_DIR": "__MEMPOOL_BACKEND_MAINNET_CACHE_DIR__",
|
||||
"CLEAR_PROTECTION_MINUTES": __MEMPOOL_BACKEND_CLEAR_PROTECTION_MINUTES__
|
||||
"NETWORK": "__MEMPOOL_NETWORK__",
|
||||
"BACKEND": "__MEMPOOL_BACKEND__",
|
||||
"HTTP_PORT": __MEMPOOL_HTTP_PORT__,
|
||||
"SPAWN_CLUSTER_PROCS": __MEMPOOL_SPAWN_CLUSTER_PROCS__,
|
||||
"API_URL_PREFIX": "__MEMPOOL_API_URL_PREFIX__",
|
||||
"POLL_RATE_MS": __MEMPOOL_POLL_RATE_MS__,
|
||||
"CACHE_DIR": "__MEMPOOL_CACHE_DIR__",
|
||||
"CLEAR_PROTECTION_MINUTES": __MEMPOOL_CLEAR_PROTECTION_MINUTES__,
|
||||
"RECOMMENDED_FEE_PERCENTILE": __MEMPOOL_RECOMMENDED_FEE_PERCENTILE__,
|
||||
"BLOCK_WEIGHT_UNITS": __MEMPOOL_BLOCK_WEIGHT_UNITS__,
|
||||
"INITIAL_BLOCKS_AMOUNT": __MEMPOOL_INITIAL_BLOCKS_AMOUNT__,
|
||||
"MEMPOOL_BLOCKS_AMOUNT": __MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__,
|
||||
"PRICE_FEED_UPDATE_INTERVAL": __MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__,
|
||||
"USE_SECOND_NODE_FOR_MINFEE": __MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__,
|
||||
"EXTERNAL_ASSETS": __MEMPOOL_EXTERNAL_ASSETS__
|
||||
},
|
||||
"CORE_RPC": {
|
||||
"HOST": "__BITCOIN_MAINNET_RPC_HOST__",
|
||||
"PORT": __BITCOIN_MAINNET_RPC_PORT__,
|
||||
"USERNAME": "__BITCOIN_MAINNET_RPC_USER__",
|
||||
"PASSWORD": "__BITCOIN_MAINNET_RPC_PASS__"
|
||||
"HOST": "__CORE_RPC_HOST__",
|
||||
"PORT": __CORE_RPC_PORT__,
|
||||
"USERNAME": "__CORE_RPC_USERNAME__",
|
||||
"PASSWORD": "__CORE_RPC_PASSWORD__"
|
||||
},
|
||||
"ELECTRUM": {
|
||||
"HOST": "__ELECTRUM_MAINNET_HTTP_HOST__",
|
||||
"PORT": __ELECTRUM_MAINNET_HTTP_PORT__,
|
||||
"TLS_ENABLED": __ELECTRUM_MAINNET_TLS_ENABLED__
|
||||
"HOST": "__ELECTRUM_HOST__",
|
||||
"PORT": __ELECTRUM_PORT__,
|
||||
"TLS_ENABLED": __ELECTRUM_TLS_ENABLED__
|
||||
},
|
||||
"ESPLORA": {
|
||||
"REST_API_URL": "http://127.0.0.1:3000"
|
||||
"REST_API_URL": "__ESPLORA_REST_API_URL__"
|
||||
},
|
||||
"SECOND_CORE_RPC": {
|
||||
"HOST": "__SECOND_CORE_RPC_HOST__",
|
||||
"PORT": __SECOND_CORE_RPC_PORT__,
|
||||
"USERNAME": "__SECOND_CORE_RPC_USERNAME__",
|
||||
"PASSWORD": "__SECOND_CORE_RPC_PASSWORD__"
|
||||
},
|
||||
"DATABASE": {
|
||||
"ENABLED": true,
|
||||
"HOST": "__MYSQL_HOST__",
|
||||
"PORT": __MYSQL_PORT__,
|
||||
"DATABASE": "__MYSQL_DATABASE__",
|
||||
"USERNAME": "__MYSQL_USERNAME__",
|
||||
"PASSWORD": "__MYSQL_PASSWORD__"
|
||||
"ENABLED": __DATABASE_ENABLED__,
|
||||
"HOST": "__DATABASE_HOST__",
|
||||
"PORT": __DATABASE_PORT__,
|
||||
"DATABASE": "__DATABASE_DATABASE__",
|
||||
"USERNAME": "__DATABASE_USERNAME__",
|
||||
"PASSWORD": "__DATABASE_PASSWORD__"
|
||||
},
|
||||
"SYSLOG": {
|
||||
"ENABLED": __SYSLOG_ENABLED__,
|
||||
"HOST": "__SYSLOG_HOST__",
|
||||
"PORT": __SYSLOG_PORT__,
|
||||
"MIN_PRIORITY": "__SYSLOG_MIN_PRIORITY__",
|
||||
"FACILITY": "__SYSLOG_FACILITY__"
|
||||
},
|
||||
"STATISTICS": {
|
||||
"ENABLED": true,
|
||||
"TX_PER_SECOND_SAMPLE_PERIOD": 150
|
||||
"ENABLED": __STATISTICS_ENABLED__,
|
||||
"TX_PER_SECOND_SAMPLE_PERIOD": __STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__
|
||||
},
|
||||
"BISQ": {
|
||||
"ENABLED": __BISQ_ENABLED__,
|
||||
"DATA_PATH": "__BISQ_DATA_PATH__"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,116 @@
|
||||
#!/bin/sh
|
||||
|
||||
#MEMPOOL
|
||||
__MEMPOOL_BACKEND_MAINNET_HTTP_PORT__=${BACKEND_MAINNET_HTTP_PORT:=8999}
|
||||
__MEMPOOL_BACKEND_MAINNET_CACHE_DIR__=${CACHE_DIR:=./cache}
|
||||
__MEMPOOL_BACKEND_CLEAR_PROTECTION_MINUTES__=${MEMPOOL_CLEAR_PROTECTION_MINUTES:=20}
|
||||
# BITCOIN
|
||||
__BITCOIN_MAINNET_RPC_HOST__=${RPC_HOST:=127.0.0.1}
|
||||
__BITCOIN_MAINNET_RPC_PORT__=${RPC_PORT:=8332}
|
||||
__BITCOIN_MAINNET_RPC_USER__=${RPC_USER:=mempool}
|
||||
__BITCOIN_MAINNET_RPC_PASS__=${RPC_PASS:=mempool}
|
||||
# MEMPOOL
|
||||
__MEMPOOL_NETWORK__=${MEMPOOL_NETWORK:=mainnet}
|
||||
__MEMPOOL_BACKEND__=${MEMPOOL_BACKEND:=electrum}
|
||||
__MEMPOOL_HTTP_PORT__=${BACKEND_HTTP_PORT:=8999}
|
||||
__MEMPOOL_SPAWN_CLUSTER_PROCS__=${MEMPOOL_SPAWN_CLUSTER_PROCS:=0}
|
||||
__MEMPOOL_API_URL_PREFIX__=${MEMPOOL_API_URL_PREFIX:=/api/v1/}
|
||||
__MEMPOOL_POLL_RATE_MS__=${MEMPOOL_POLL_RATE_MS:=2000}
|
||||
__MEMPOOL_CACHE_DIR__=${MEMPOOL_CACHE_DIR:=./cache}
|
||||
__MEMPOOL_CLEAR_PROTECTION_MINUTES__=${MEMPOOL_CLEAR_PROTECTION_MINUTES:=20}
|
||||
__MEMPOOL_RECOMMENDED_FEE_PERCENTILE__=${MEMPOOL_RECOMMENDED_FEE_PERCENTILE:=50}
|
||||
__MEMPOOL_BLOCK_WEIGHT_UNITS__=${MEMPOOL_BLOCK_WEIGHT_UNITS:=4000000}
|
||||
__MEMPOOL_INITIAL_BLOCKS_AMOUNT__=${MEMPOOL_INITIAL_BLOCKS_AMOUNT:=8}
|
||||
__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__=${MEMPOOL_MEMPOOL_BLOCKS_AMOUNT:=8}
|
||||
__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__=${MEMPOOL_PRICE_FEED_UPDATE_INTERVAL:=3600}
|
||||
__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__=${MEMPOOL_USE_SECOND_NODE_FOR_MINFEE:=false}
|
||||
__MEMPOOL_EXTERNAL_ASSETS__=${MEMPOOL_EXTERNAL_ASSETS:=[]}
|
||||
|
||||
# CORE_RPC
|
||||
__CORE_RPC_HOST__=${CORE_RPC_HOST:=127.0.0.1}
|
||||
__CORE_RPC_PORT__=${CORE_RPC_PORT:=8332}
|
||||
__CORE_RPC_USERNAME__=${CORE_RPC_USERNAME:=mempool}
|
||||
__CORE_RPC_PASSWORD__=${CORE_RPC_PASSWORD:=mempool}
|
||||
|
||||
# ELECTRUM
|
||||
__ELECTRUM_MAINNET_HTTP_HOST__=${ELECTRUM_HOST:=127.0.0.1}
|
||||
__ELECTRUM_MAINNET_HTTP_PORT__=${ELECTRUM_PORT:=50002} # 50001?
|
||||
__ELECTRUM_MAINNET_TLS_ENABLED__=${ELECTRUM_TLS:=false}
|
||||
# MYSQL
|
||||
__MYSQL_HOST__=${MYSQL_HOST:=127.0.0.1}
|
||||
__MYSQL_PORT__=${MYSQL_PORT:=3306}
|
||||
__MYSQL_DATABASE__=${MYSQL_DATABASE:=mempool}
|
||||
__MYSQL_USERNAME__=${MYSQL_USER:=mempool}
|
||||
__MYSQL_PASSWORD__=${MYSQL_PASS:=mempool}
|
||||
__ELECTRUM_HOST__=${ELECTRUM_HOST:=127.0.0.1}
|
||||
__ELECTRUM_PORT__=${ELECTRUM_PORT:=50002}
|
||||
__ELECTRUM_TLS_ENABLED__=${ELECTRUM_TLS:=false}
|
||||
|
||||
mkdir -p "${__MEMPOOL_BACKEND_MAINNET_CACHE_DIR__}"
|
||||
# ESPLORA
|
||||
__ESPLORA_REST_API_URL__=${ESPLORA_REST_API_URL:=http://127.0.0.1:3000}
|
||||
|
||||
sed -i "s/__BITCOIN_MAINNET_RPC_HOST__/${__BITCOIN_MAINNET_RPC_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__BITCOIN_MAINNET_RPC_PORT__/${__BITCOIN_MAINNET_RPC_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__BITCOIN_MAINNET_RPC_USER__/${__BITCOIN_MAINNET_RPC_USER__}/g" mempool-config.json
|
||||
sed -i "s/__BITCOIN_MAINNET_RPC_PASS__/${__BITCOIN_MAINNET_RPC_PASS__}/g" mempool-config.json
|
||||
sed -i "s/__ELECTRUM_MAINNET_HTTP_HOST__/${__ELECTRUM_MAINNET_HTTP_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__ELECTRUM_MAINNET_HTTP_PORT__/${__ELECTRUM_MAINNET_HTTP_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__ELECTRUM_MAINNET_TLS_ENABLED__/${__ELECTRUM_MAINNET_TLS_ENABLED__}/g" mempool-config.json
|
||||
sed -i "s/__MYSQL_HOST__/${__MYSQL_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__MYSQL_PORT__/${__MYSQL_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__MYSQL_DATABASE__/${__MYSQL_DATABASE__}/g" mempool-config.json
|
||||
sed -i "s/__MYSQL_USERNAME__/${__MYSQL_USERNAME__}/g" mempool-config.json
|
||||
sed -i "s/__MYSQL_PASSWORD__/${__MYSQL_PASSWORD__}/g" mempool-config.json
|
||||
sed -i "s!__MEMPOOL_BACKEND_MAINNET_CACHE_DIR__!${__MEMPOOL_BACKEND_MAINNET_CACHE_DIR__}!g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_BACKEND_MAINNET_HTTP_PORT__/${__MEMPOOL_BACKEND_MAINNET_HTTP_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_BACKEND_CLEAR_PROTECTION_MINUTES__/${__MEMPOOL_BACKEND_CLEAR_PROTECTION_MINUTES__}/g" mempool-config.json
|
||||
# SECOND_CORE_RPC
|
||||
__SECOND_CORE_RPC_HOST__=${SECOND_CORE_RPC_HOST:=127.0.0.1}
|
||||
__SECOND_CORE_RPC_PORT__=${SECOND_CORE_RPC_PORT:=8332}
|
||||
__SECOND_CORE_RPC_USERNAME__=${SECOND_CORE_RPC_USERNAME:=mempool}
|
||||
__SECOND_CORE_RPC_PASSWORD__=${SECOND_CORE_RPC_PASSWORD:=mempool}
|
||||
|
||||
# DATABASE
|
||||
__DATABASE_ENABLED__=${DATABASE_ENABLED:=true}
|
||||
__DATABASE_HOST__=${DATABASE_HOST:=127.0.0.1}
|
||||
__DATABASE_PORT__=${DATABASE_PORT:=3306}
|
||||
__DATABASE_DATABASE__=${DATABASE_DATABASE:=mempool}
|
||||
__DATABASE_USERNAME__=${DATABASE_USERNAME:=mempool}
|
||||
__DATABASE_PASSWORD__=${DATABASE_PASSWORD:=mempool}
|
||||
|
||||
# SYSLOG
|
||||
__SYSLOG_ENABLED__=${SYSLOG_ENABLED:=false}
|
||||
__SYSLOG_HOST__=${SYSLOG_HOST:=127.0.0.1}
|
||||
__SYSLOG_PORT__=${SYSLOG_PORT:=514}
|
||||
__SYSLOG_MIN_PRIORITY__=${SYSLOG_MIN_PRIORITY:=info}
|
||||
__SYSLOG_FACILITY__=${SYSLOG_FACILITY:=local7}
|
||||
|
||||
# STATISTICS
|
||||
__STATISTICS_ENABLED__=${STATISTICS_ENABLED:=true}
|
||||
__STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__=${STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD:=150}
|
||||
|
||||
# BISQ
|
||||
__BISQ_ENABLED__=${BISQ_ENABLED:=false}
|
||||
__BISQ_DATA_PATH__=${BISQ_DATA_PATH:=/bisq/statsnode-data/btc_mainnet/db}
|
||||
|
||||
mkdir -p "${__MEMPOOL_CACHE_DIR__}"
|
||||
|
||||
sed -i "s/__MEMPOOL_NETWORK__/${__MEMPOOL_NETWORK__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_BACKEND__/${__MEMPOOL_BACKEND__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_HTTP_PORT__/${__MEMPOOL_HTTP_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_SPAWN_CLUSTER_PROCS__/${__MEMPOOL_SPAWN_CLUSTER_PROCS__}/g" mempool-config.json
|
||||
sed -i "s!__MEMPOOL_API_URL_PREFIX__!${__MEMPOOL_API_URL_PREFIX__}!g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_POLL_RATE_MS__/${__MEMPOOL_POLL_RATE_MS__}/g" mempool-config.json
|
||||
sed -i "s!__MEMPOOL_CACHE_DIR__!${__MEMPOOL_CACHE_DIR__}!g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_CLEAR_PROTECTION_MINUTES__/${__MEMPOOL_CLEAR_PROTECTION_MINUTES__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_RECOMMENDED_FEE_PERCENTILE__/${__MEMPOOL_RECOMMENDED_FEE_PERCENTILE__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_BLOCK_WEIGHT_UNITS__/${__MEMPOOL_BLOCK_WEIGHT_UNITS__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_INITIAL_BLOCKS_AMOUNT__/${__MEMPOOL_INITIAL_BLOCKS_AMOUNT__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__/${__MEMPOOL_MEMPOOL_BLOCKS_AMOUNT__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__/${__MEMPOOL_PRICE_FEED_UPDATE_INTERVAL__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__/${__MEMPOOL_USE_SECOND_NODE_FOR_MINFEE__}/g" mempool-config.json
|
||||
sed -i "s/__MEMPOOL_EXTERNAL_ASSETS__/${__MEMPOOL_EXTERNAL_ASSETS__}/g" mempool-config.json
|
||||
|
||||
sed -i "s/__CORE_RPC_HOST__/${__CORE_RPC_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__CORE_RPC_PORT__/${__CORE_RPC_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__CORE_RPC_USERNAME__/${__CORE_RPC_USERNAME__}/g" mempool-config.json
|
||||
sed -i "s/__CORE_RPC_PASSWORD__/${__CORE_RPC_PASSWORD__}/g" mempool-config.json
|
||||
|
||||
sed -i "s/__ELECTRUM_HOST__/${__ELECTRUM_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__ELECTRUM_PORT__/${__ELECTRUM_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__ELECTRUM_TLS_ENABLED__/${__ELECTRUM_TLS_ENABLED__}/g" mempool-config.json
|
||||
|
||||
sed -i "s!__ESPLORA_REST_API_URL__!${__ESPLORA_REST_API_URL__}!g" mempool-config.json
|
||||
|
||||
sed -i "s/__SECOND_CORE_RPC_HOST__/${__SECOND_CORE_RPC_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__SECOND_CORE_RPC_PORT__/${__SECOND_CORE_RPC_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__SECOND_CORE_RPC_USERNAME__/${__SECOND_CORE_RPC_USERNAME__}/g" mempool-config.json
|
||||
sed -i "s/__SECOND_CORE_RPC_PASSWORD__/${__SECOND_CORE_RPC_PASSWORD__}/g" mempool-config.json
|
||||
|
||||
sed -i "s/__DATABASE_ENABLED__/${__DATABASE_ENABLED__}/g" mempool-config.json
|
||||
sed -i "s/__DATABASE_HOST__/${__DATABASE_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__DATABASE_PORT__/${__DATABASE_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__DATABASE_DATABASE__/${__DATABASE_DATABASE__}/g" mempool-config.json
|
||||
sed -i "s/__DATABASE_USERNAME__/${__DATABASE_USERNAME__}/g" mempool-config.json
|
||||
sed -i "s/__DATABASE_PASSWORD__/${__DATABASE_PASSWORD__}/g" mempool-config.json
|
||||
|
||||
sed -i "s/__SYSLOG_ENABLED__/${__SYSLOG_ENABLED__}/g" mempool-config.json
|
||||
sed -i "s/__SYSLOG_HOST__/${__SYSLOG_HOST__}/g" mempool-config.json
|
||||
sed -i "s/__SYSLOG_PORT__/${__SYSLOG_PORT__}/g" mempool-config.json
|
||||
sed -i "s/__SYSLOG_MIN_PRIORITY__/${__SYSLOG_MIN_PRIORITY__}/g" mempool-config.json
|
||||
sed -i "s/__SYSLOG_FACILITY__/${__SYSLOG_FACILITY__}/g" mempool-config.json
|
||||
|
||||
sed -i "s/__STATISTICS_ENABLED__/${__STATISTICS_ENABLED__}/g" mempool-config.json
|
||||
sed -i "s/__STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__/${__STATISTICS_TX_PER_SECOND_SAMPLE_PERIOD__}/g" mempool-config.json
|
||||
|
||||
sed -i "s/__BISQ_ENABLED__/${__BISQ_ENABLED__}/g" mempool-config.json
|
||||
sed -i "s!__BISQ_DATA_PATH__!${__BISQ_DATA_PATH__}!g" mempool-config.json
|
||||
|
||||
node /backend/dist/index.js
|
||||
|
||||
0
docker/data/.gitkeep
Normal file
0
docker/data/.gitkeep
Normal file
@@ -1,23 +1,10 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
|
||||
electrum:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/electrum/Dockerfile
|
||||
user: "1000:1000"
|
||||
restart: on-failure
|
||||
command: ""
|
||||
ports:
|
||||
- 50001:50001
|
||||
- 50002:50002
|
||||
- 4224:4224
|
||||
- 8332:8332
|
||||
environment:
|
||||
ELECTRUM: "electrum"
|
||||
# add electrs configs
|
||||
web:
|
||||
environment:
|
||||
FRONTEND_HTTP_PORT: "8080"
|
||||
BACKEND_MAINNET_HTTP_HOST: "api"
|
||||
image: mempool/frontend:latest
|
||||
user: "1000:1000"
|
||||
restart: on-failure
|
||||
@@ -25,10 +12,19 @@ services:
|
||||
command: "./wait-for db:3306 --timeout=720 -- nginx -g 'daemon off;'"
|
||||
ports:
|
||||
- 80:8080
|
||||
environment:
|
||||
FRONTEND_HTTP_PORT: "8080"
|
||||
BACKEND_MAINNET_HTTP_HOST: "api"
|
||||
api:
|
||||
environment:
|
||||
MEMPOOL_BACKEND: "none"
|
||||
CORE_RPC_HOST: "172.27.0.1"
|
||||
CORE_RPC_PORT: "8332"
|
||||
CORE_RPC_USERNAME: "mempool"
|
||||
CORE_RPC_PASSWORD: "mempool"
|
||||
DATABASE_ENABLED: "true"
|
||||
DATABASE_HOST: "db"
|
||||
DATABASE_DATABASE: "mempool"
|
||||
DATABASE_USERNAME: "mempool"
|
||||
DATABASE_PASSWORD: "mempool"
|
||||
STATISTICS_ENABLED: "true"
|
||||
image: mempool/backend:latest
|
||||
user: "1000:1000"
|
||||
restart: on-failure
|
||||
@@ -36,32 +32,15 @@ services:
|
||||
command: "./wait-for-it.sh db:3306 --timeout=720 --strict -- ./start.sh"
|
||||
volumes:
|
||||
- ./data:/backend/cache
|
||||
db:
|
||||
environment:
|
||||
RPC_HOST: "127.0.0.1"
|
||||
RPC_PORT: "8332"
|
||||
RPC_USER: "mempool"
|
||||
RPC_PASS: "mempool"
|
||||
ELECTRUM_HOST: "127.0.0.1"
|
||||
ELECTRUM_PORT: "50002"
|
||||
ELECTRUM_TLS: "false"
|
||||
MYSQL_HOST: "db"
|
||||
MYSQL_PORT: "3306"
|
||||
MYSQL_DATABASE: "mempool"
|
||||
MYSQL_USER: "mempool"
|
||||
MYSQL_PASS: "mempool"
|
||||
BACKEND_MAINNET_HTTP_PORT: "8999"
|
||||
CACHE_DIR: "/backend/cache"
|
||||
MEMPOOL_CLEAR_PROTECTION_MINUTES: "20"
|
||||
db:
|
||||
MYSQL_PASSWORD: "mempool"
|
||||
MYSQL_ROOT_PASSWORD: "admin"
|
||||
image: mariadb:10.5.8
|
||||
user: "1000:1000"
|
||||
restart: on-failure
|
||||
stop_grace_period: 1m
|
||||
volumes:
|
||||
- ./mysql/data:/var/lib/mysql
|
||||
- ./mysql/db-scripts:/docker-entrypoint-initdb.d
|
||||
environment:
|
||||
MYSQL_DATABASE: "mempool"
|
||||
MYSQL_USER: "mempool"
|
||||
MYSQL_PASSWORD: "mempool"
|
||||
MYSQL_ROOT_PASSWORD: "admin"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#backend
|
||||
gitMaster="\.\.\/\.git\/refs\/heads\/master"
|
||||
git ls-remote https://github.com/mempool/mempool.git $1 | awk '{ print $1}' > ./backend/master
|
||||
git ls-remote https://github.com/mempool/mempool.git "$1^{}" | awk '{ print $1}' > ./backend/master
|
||||
cp ./docker/backend/* ./backend/
|
||||
sed -i "s/${gitMaster}/master/g" ./backend/src/api/backend-info.ts
|
||||
|
||||
|
||||
0
docker/mysql/data/.gitkeep
Normal file
0
docker/mysql/data/.gitkeep
Normal file
3
frontend/.gitignore
vendored
3
frontend/.gitignore
vendored
@@ -34,6 +34,7 @@ speed-measure-plugin.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
@@ -49,6 +50,8 @@ Thumbs.db
|
||||
|
||||
src/resources/assets.json
|
||||
src/resources/assets.minimal.json
|
||||
src/resources/assets-testnet.json
|
||||
src/resources/assets-testnet.minimal.json
|
||||
src/resources/pools.json
|
||||
|
||||
# environment config
|
||||
|
||||
@@ -255,20 +255,6 @@
|
||||
"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": {
|
||||
"builder": "@cypress/schematic:cypress",
|
||||
"options": {
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"retries": {
|
||||
"runMode": 3,
|
||||
"openMode": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"chromeWebSecurity": false
|
||||
}
|
||||
|
||||
52
frontend/cypress/fixtures/mainnet_rbf.json
Normal file
52
frontend/cypress/fixtures/mainnet_rbf.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"rbfTransaction": {
|
||||
"txid": "8913ec7ba0ede285dbd120e46f6d61a28f2903c10814a6f6c4f97d0edf3e1f46",
|
||||
"version": 2,
|
||||
"locktime": 632699,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "02238126a63ea2669c5f378012180ef8b54402a949316f9b2f1352c51730a086",
|
||||
"vout": 0,
|
||||
"prevout": {
|
||||
"scriptpubkey": "a914f8e495456956c833e5e8c69b9a9dc041aa14c72f87",
|
||||
"scriptpubkey_asm": "OP_HASH160 OP_PUSHBYTES_20 f8e495456956c833e5e8c69b9a9dc041aa14c72f OP_EQUAL",
|
||||
"scriptpubkey_type": "p2sh",
|
||||
"scriptpubkey_address": "3QP3LMD8veT5GtWV83Nosif2Bhr73857VB",
|
||||
"value": 25000000
|
||||
},
|
||||
"scriptsig": "22002043288fbbc0fc5efa86c229dbb7d88ab78d57957c65b5d5ceaece70838976ad1b",
|
||||
"scriptsig_asm": "OP_PUSHBYTES_34 002043288fbbc0fc5efa86c229dbb7d88ab78d57957c65b5d5ceaece70838976ad1b",
|
||||
"witness": [
|
||||
"",
|
||||
"3044022009e2d3a8e645f65bc89c8492cd9c08e6fb02609fd402214884a754a1970145340220575bb325429def59f3a3f1e22d9740a3feecbe97438ff3bb5796b2c46b3c477f01",
|
||||
"3044022039c34372882da8fc1c1243bd72b5e7e5e6870301ef56bdebb87bc647fb50f9b5022071a704ee77d742f78b10e45be675d4c45a5f31e884139e75c975144fde70e41701",
|
||||
"522102346eb7133f11e0dc279bc592d5ac948a91676372a6144c9ae2085625d7fbf70421021b9508a458f9d59be4eb8cc87ad582c3b494106fb1d4ec22801569be0700eb7b52ae"
|
||||
],
|
||||
"is_coinbase": false,
|
||||
"sequence": 4294967293,
|
||||
"inner_redeemscript_asm": "OP_0 OP_PUSHBYTES_32 43288fbbc0fc5efa86c229dbb7d88ab78d57957c65b5d5ceaece70838976ad1b",
|
||||
"inner_witnessscript_asm": "OP_PUSHNUM_2 OP_PUSHBYTES_33 02346eb7133f11e0dc279bc592d5ac948a91676372a6144c9ae2085625d7fbf704 OP_PUSHBYTES_33 021b9508a458f9d59be4eb8cc87ad582c3b494106fb1d4ec22801569be0700eb7b OP_PUSHNUM_2 OP_CHECKMULTISIG"
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"scriptpubkey": "a914fd4e5e59dd5cf2dc48eaedf1a2a1650ca1ce9d7f87",
|
||||
"scriptpubkey_asm": "OP_HASH160 OP_PUSHBYTES_20 fd4e5e59dd5cf2dc48eaedf1a2a1650ca1ce9d7f OP_EQUAL",
|
||||
"scriptpubkey_type": "p2sh",
|
||||
"scriptpubkey_address": "3QnNmDhZS7toHA7bhhbTPBdtpLJoeecq5c",
|
||||
"value": 13986350
|
||||
},
|
||||
{
|
||||
"scriptpubkey": "76a914edc93d0446deec1c2d514f3a490f050096e74e0e88ac",
|
||||
"scriptpubkey_asm": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 edc93d0446deec1c2d514f3a490f050096e74e0e OP_EQUALVERIFY OP_CHECKSIG",
|
||||
"scriptpubkey_type": "p2pkh",
|
||||
"scriptpubkey_address": "1NgJDkTUqJxxCAAZrrsC87kWag5kphrRtM",
|
||||
"value": 11000000
|
||||
}
|
||||
],
|
||||
"size": 372,
|
||||
"weight": 828,
|
||||
"fee": 1.5,
|
||||
"status": { "confirmed": false }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
describe('Bisq', () => {
|
||||
const baseModule = Cypress.env("BASE_MODULE");
|
||||
const basePath = (baseModule === 'bisq') ? '' : '/bisq';
|
||||
const basePath = '';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('/sockjs-node/info*').as('socket');
|
||||
@@ -23,7 +23,7 @@ describe('Bisq', () => {
|
||||
});
|
||||
});
|
||||
|
||||
if (baseModule === 'mempool' || baseModule === 'bisq') {
|
||||
if (baseModule === 'bisq') {
|
||||
|
||||
it('loads the dashboard', () => {
|
||||
cy.visit(`${basePath}`);
|
||||
@@ -59,9 +59,8 @@ describe('Bisq', () => {
|
||||
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');
|
||||
cy.get('.section-header').should('have.length.at.least', 1);
|
||||
cy.get('.endpoint-container').should('have.length.at.least', 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
describe('Liquid', () => {
|
||||
const baseModule = Cypress.env("BASE_MODULE");
|
||||
const basePath = (baseModule === 'liquid') ? '' : '/liquid';
|
||||
const basePath = '';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('/liquid/api/block/**').as('block');
|
||||
@@ -16,7 +16,7 @@ describe('Liquid', () => {
|
||||
});
|
||||
});
|
||||
|
||||
if (baseModule === 'mempool' || baseModule === 'liquid') {
|
||||
if (baseModule === 'liquid') {
|
||||
|
||||
it('check first mempool block after skeleton loads', () => {
|
||||
cy.visit(`${basePath}`);
|
||||
@@ -62,6 +62,54 @@ describe('Liquid', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders unconfidential addresses correctly on mobile', () => {
|
||||
cy.viewport('iphone-6');
|
||||
cy.visit(`${basePath}/address/ex1qqmmjdwrlg59c8q4l75sj6wedjx57tj5grt8pat`);
|
||||
cy.waitForSkeletonGone();
|
||||
//TODO: Add proper IDs for these selectors
|
||||
const firstRowSelector = '.container-xl > :nth-child(3) > div > :nth-child(1) > .table > tbody';
|
||||
const thirdRowSelector = '.container-xl > :nth-child(3) > div > :nth-child(3)';
|
||||
cy.get(firstRowSelector).invoke('css', 'width').then(firstRowWidth => {
|
||||
cy.get(thirdRowSelector).invoke('css', 'width').then(thirdRowWidth => {
|
||||
expect(parseInt(firstRowWidth)).to.be.lessThan(parseInt(thirdRowWidth));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('peg in/peg out', () => {
|
||||
it('loads peg in addresses', () => {
|
||||
cy.visit(`${basePath}/tx/fe764f7bedfc2a37b29d9c8aef67d64a57d253a6b11c5a55555cfd5826483a58`);
|
||||
cy.waitForSkeletonGone();
|
||||
//TODO: Change to an element id so we don't assert on a string
|
||||
cy.get('#table-tx-vin').should('contain', 'Peg-in');
|
||||
cy.get('#table-tx-vin a').click().then(() => {
|
||||
cy.waitForSkeletonGone();
|
||||
if (baseModule === 'liquid') {
|
||||
cy.url().should('eq', 'https://mempool.space/tx/f148c0d854db4174ea420655235f910543f0ec3680566dcfdf84fb0a1697b592');
|
||||
} else {
|
||||
//TODO: Use an environment variable to get the hostname
|
||||
cy.url().should('eq', 'http://localhost:4200/tx/f148c0d854db4174ea420655235f910543f0ec3680566dcfdf84fb0a1697b592');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('loads peg out addresses', () => {
|
||||
cy.visit(`${basePath}/tx/ecf6eba04ffb3946faa172343c87162df76f1a57b07b0d6dc6ad956b13376dc8`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#table-tx-vout a').first().click().then(() => {
|
||||
cy.waitForSkeletonGone();
|
||||
if (baseModule === 'liquid') {
|
||||
cy.url().should('eq', 'https://mempool.space/address/1BxoGcMg14oaH3CwHD2hF4gU9VcfgX5yoR');
|
||||
} else {
|
||||
//TODO: Use an environment variable to get the hostname
|
||||
cy.url().should('eq', 'http://localhost:4200/address/1BxoGcMg14oaH3CwHD2hF4gU9VcfgX5yoR');
|
||||
}
|
||||
//TODO: Add a custom class so we don't assert on a string
|
||||
cy.get('.badge').should('contain','Liquid Peg Out');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('assets', () => {
|
||||
it('shows the assets screen', () => {
|
||||
cy.visit(`${basePath}/assets`);
|
||||
|
||||
@@ -2,6 +2,36 @@ import { emitMempoolInfo, dropWebSocket } from "../../support/websocket";
|
||||
|
||||
const baseModule = Cypress.env("BASE_MODULE");
|
||||
|
||||
|
||||
//Credit: https://github.com/bahmutov/cypress-examples/blob/6cedb17f83a3bb03ded13cf1d6a3f0656ca2cdf5/docs/recipes/overlapping-elements.md
|
||||
|
||||
/**
|
||||
* Returns true if two DOM rectangles are overlapping
|
||||
* @param {DOMRect} rect1 the bounding client rectangle of the first element
|
||||
* @param {DOMRect} rect2 the bounding client rectangle of the second element
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const areOverlapping = (rect1, rect2) => {
|
||||
// if one rectangle is on the left side of the other
|
||||
if (rect1.right < rect2.left || rect2.right < rect1.left) {
|
||||
return false
|
||||
}
|
||||
|
||||
// if one rectangle is above the other
|
||||
if (rect1.bottom < rect2.top || rect2.bottom < rect1.top) {
|
||||
return false
|
||||
}
|
||||
|
||||
// the rectangles must overlap
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding rectangle of the first DOM
|
||||
* element in the given jQuery object.
|
||||
*/
|
||||
const getRectangle = ($el) => $el[0].getBoundingClientRect();
|
||||
|
||||
describe('Mainnet', () => {
|
||||
beforeEach(() => {
|
||||
//cy.intercept('/sockjs-node/info*').as('socket');
|
||||
@@ -56,7 +86,7 @@ describe('Mainnet', () => {
|
||||
cy.get('.badge', {timeout: 25000}).should('not.exist');
|
||||
emitMempoolInfo({
|
||||
'params': {
|
||||
loaded: true
|
||||
command: 'init'
|
||||
}
|
||||
});
|
||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('not.exist');
|
||||
@@ -266,9 +296,7 @@ describe('Mainnet', () => {
|
||||
|
||||
cy.changeNetwork("testnet");
|
||||
cy.changeNetwork("signet");
|
||||
cy.changeNetwork("liquid");
|
||||
cy.changeNetwork("mainnet");
|
||||
cy.changeNetwork("bisq");
|
||||
});
|
||||
|
||||
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||
@@ -283,7 +311,7 @@ describe('Mainnet', () => {
|
||||
|
||||
emitMempoolInfo({
|
||||
'params': {
|
||||
loaded: true
|
||||
command: 'init'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -428,6 +456,78 @@ describe('Mainnet', () => {
|
||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RBF transactions', () => {
|
||||
it('shows RBF transactions properly (mobile)', () => {
|
||||
cy.viewport('iphone-xr');
|
||||
cy.mockMempoolSocket();
|
||||
cy.visit('/tx/f81a08699b62b2070ad8fe0f2a076f8bea0386a2fdcd8124caee42cbc564a0d5');
|
||||
|
||||
cy.waitForSkeletonGone();
|
||||
|
||||
emitMempoolInfo({
|
||||
'params': {
|
||||
command: 'init'
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('#mempool-block-0');
|
||||
|
||||
emitMempoolInfo({
|
||||
'params': {
|
||||
command: 'rbfTransaction'
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('.alert-mempool').should('be.visible');
|
||||
cy.get('.alert-mempool').invoke('css', 'width').then((alertWidth) => {
|
||||
cy.get('.container-xl > :nth-child(3)').invoke('css', 'width').should('equal', alertWidth);
|
||||
});
|
||||
|
||||
cy.get('.btn-success').then(getRectangle).then((rectA) => {
|
||||
cy.get('.alert-mempool').then(getRectangle).then((rectB) => {
|
||||
expect(areOverlapping(rectA, rectB), 'Confirmations box and RBF alert are overlapping').to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('shows RBF transactions properly (desktop)', () => {
|
||||
cy.viewport('macbook-16');
|
||||
cy.mockMempoolSocket();
|
||||
cy.visit('/tx/f81a08699b62b2070ad8fe0f2a076f8bea0386a2fdcd8124caee42cbc564a0d5');
|
||||
|
||||
cy.waitForSkeletonGone();
|
||||
|
||||
emitMempoolInfo({
|
||||
'params': {
|
||||
command: 'init'
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('#mempool-block-0');
|
||||
|
||||
emitMempoolInfo({
|
||||
'params': {
|
||||
command: 'rbfTransaction'
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('.alert-mempool').should('be.visible');
|
||||
|
||||
const alertLocator = '.alert-mempool';
|
||||
const tableLocator = '.container-xl > :nth-child(3)';
|
||||
|
||||
cy.get(tableLocator).invoke('css', 'width').then((firstWidth) => {
|
||||
cy.get(alertLocator).invoke('css', 'width').should('equal', firstWidth);
|
||||
});
|
||||
|
||||
cy.get('.btn-success').then(getRectangle).then((rectA) => {
|
||||
cy.get('.alert-mempool').then(getRectangle).then((rectB) => {
|
||||
expect(areOverlapping(rectA, rectB), 'Confirmations box and RBF alert are overlapping').to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||
}
|
||||
|
||||
@@ -31,19 +31,19 @@ export const mockWebSocket = () => {
|
||||
cy.on('window:before:load', (win) => {
|
||||
const winWebSocket = win.WebSocket;
|
||||
cy.stub(win, 'WebSocket').callsFake((url) => {
|
||||
console.log(url);
|
||||
console.log(url);
|
||||
if ((new URL(url).pathname.indexOf('/sockjs-node/') !== 0)) {
|
||||
const { server, websocket } = createMock(url);
|
||||
|
||||
win.mockServer = server;
|
||||
win.mockServer.on('connection', (socket) => {
|
||||
win.mockSocket = socket;
|
||||
win.mockSocket.send('{"action":"init"}');
|
||||
win.mockSocket.send('{"action":"init"}');
|
||||
});
|
||||
|
||||
win.mockServer.on('message', (message) => {
|
||||
console.log(message);
|
||||
});
|
||||
win.mockServer.on('message', (message) => {
|
||||
console.log(message);
|
||||
});
|
||||
|
||||
return websocket;
|
||||
} else {
|
||||
@@ -68,7 +68,13 @@ export const emitMempoolInfo = ({
|
||||
//TODO: Use network specific mocks
|
||||
case "signet":
|
||||
case "testnet":
|
||||
case "mainnet":
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (params.command) {
|
||||
case "init": {
|
||||
win.mockSocket.send('{"action":"init"}');
|
||||
win.mockSocket.send('{"action":"want","data":["blocks","stats","mempool-blocks","live-2h-chart"]}');
|
||||
win.mockSocket.send('{"conversions":{"USD":32365.338815782445}}');
|
||||
@@ -78,6 +84,16 @@ export const emitMempoolInfo = ({
|
||||
cy.readFile('cypress/fixtures/mainnet_mempoolInfo.json', 'ascii').then((fixture) => {
|
||||
win.mockSocket.send(JSON.stringify(fixture));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "rbfTransaction": {
|
||||
cy.readFile('cypress/fixtures/mainnet_rbf.json', 'ascii').then((fixture) => {
|
||||
win.mockSocket.send(JSON.stringify(fixture));
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
cy.waitForSkeletonGone();
|
||||
@@ -89,4 +105,4 @@ export const dropWebSocket = (() => {
|
||||
win.mockServer.simulate("error");
|
||||
});
|
||||
return cy.wait(500);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"TESTNET_ENABLED": false,
|
||||
"SIGNET_ENABLED": false,
|
||||
"LIQUID_ENABLED": false,
|
||||
"LIQUID_TESTNET_ENABLED": false,
|
||||
"BISQ_ENABLED": false,
|
||||
"BISQ_SEPARATE_BACKEND": false,
|
||||
"ITEMS_PER_PAGE": 10,
|
||||
@@ -9,7 +10,10 @@
|
||||
"NGINX_PROTOCOL": "http",
|
||||
"NGINX_HOSTNAME": "127.0.0.1",
|
||||
"NGINX_PORT": "80",
|
||||
"MEMPOOL_BLOCKS_AMOUNT": 8,
|
||||
"BLOCK_WEIGHT_UNITS": 4000000,
|
||||
"BASE_MODULE": "mempool"
|
||||
"MEMPOOL_BLOCKS_AMOUNT": 8,
|
||||
"BASE_MODULE": "mempool",
|
||||
"MEMPOOL_WEBSITE_URL": "https://mempool.space",
|
||||
"LIQUID_WEBSITE_URL": "https://liquid.network",
|
||||
"BISQ_WEBSITE_URL": "https://bisq.markets"
|
||||
}
|
||||
|
||||
18328
frontend/package-lock.json
generated
18328
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mempool-frontend",
|
||||
"version": "2.3.0-dev",
|
||||
"version": "2.3.1",
|
||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||
"license": "GNU Affero General Public License v3.0",
|
||||
"homepage": "https://mempool.space",
|
||||
@@ -34,7 +34,10 @@
|
||||
"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",
|
||||
"generate-config": "node generate-config.js",
|
||||
"build-mempool.js": "tsc | browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
|
||||
"build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js && npm run build-mempool-bisq-js",
|
||||
"build-mempool-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
|
||||
"build-mempool-bisq-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-bisq.js --standalone bisqJS > ./dist/mempool/browser/en-US/bisq.js",
|
||||
"build-mempool-liquid-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-liquid.js --standalone liquidJS > ./dist/mempool/browser/en-US/liquid.js",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "npm run generate-config && ng e2e",
|
||||
@@ -53,25 +56,27 @@
|
||||
"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": {
|
||||
"@angular/animations": "~12.2.6",
|
||||
"@angular/common": "~12.2.6",
|
||||
"@angular/compiler": "~12.2.6",
|
||||
"@angular/core": "~12.2.6",
|
||||
"@angular/forms": "~12.2.6",
|
||||
"@angular/localize": "^12.2.6",
|
||||
"@angular/platform-browser": "~12.2.6",
|
||||
"@angular/platform-browser-dynamic": "~12.2.6",
|
||||
"@angular/platform-server": "~12.2.6",
|
||||
"@angular/router": "~12.2.6",
|
||||
"@angular-devkit/build-angular": "^13.1.2",
|
||||
"@angular/animations": "~13.1.1",
|
||||
"@angular/cli": "~13.0.4",
|
||||
"@angular/common": "~13.1.1",
|
||||
"@angular/compiler": "~13.1.1",
|
||||
"@angular/core": "~13.1.1",
|
||||
"@angular/forms": "~13.1.1",
|
||||
"@angular/localize": "^13.1.1",
|
||||
"@angular/platform-browser": "~13.1.1",
|
||||
"@angular/platform-browser-dynamic": "~13.1.1",
|
||||
"@angular/platform-server": "~13.1.1",
|
||||
"@angular/router": "~13.1.1",
|
||||
"@fortawesome/angular-fontawesome": "^0.8.2",
|
||||
"@fortawesome/fontawesome-common-types": "^0.2.35",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@juggle/resize-observer": "^3.3.1",
|
||||
"@mempool/mempool.js": "^2.2.4",
|
||||
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
|
||||
"@mempool/mempool.js": "2.3.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
|
||||
"@nguniversal/express-engine": "11.2.1",
|
||||
"@types/qrcode": "^1.3.4",
|
||||
"@types/qrcode": "1.4.1",
|
||||
"bootstrap": "4.5.0",
|
||||
"browserify": "^17.0.0",
|
||||
"clipboard": "^2.0.4",
|
||||
@@ -82,7 +87,7 @@
|
||||
"ngx-bootrap-multiselect": "^2.0.0",
|
||||
"ngx-echarts": "^7.0.1",
|
||||
"ngx-infinite-scroll": "^10.0.1",
|
||||
"qrcode": "^1.4.4",
|
||||
"qrcode": "1.5.0",
|
||||
"rxjs": "^6.6.7",
|
||||
"tinyify": "^3.0.0",
|
||||
"tlite": "^0.1.9",
|
||||
@@ -90,10 +95,8 @@
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^12.2.6",
|
||||
"@angular/cli": "~12.2.6",
|
||||
"@angular/compiler-cli": "~12.2.6",
|
||||
"@angular/language-service": "~12.2.6",
|
||||
"@angular/compiler-cli": "~13.1.1",
|
||||
"@angular/language-service": "~13.1.1",
|
||||
"@nguniversal/builders": "^11.2.1",
|
||||
"@types/express": "^4.17.0",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
@@ -110,12 +113,12 @@
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.3.5"
|
||||
"typescript": "~4.4.4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@cypress/schematic": "^1.3.0",
|
||||
"cypress": "^8.3.1",
|
||||
"cypress-fail-on-console-error": "^2.1.0",
|
||||
"cypress": "^9.1.1",
|
||||
"cypress-fail-on-console-error": "^2.1.3",
|
||||
"cypress-wait-until": "^1.7.1",
|
||||
"mock-socket": "^9.0.3",
|
||||
"start-server-and-test": "^1.12.6"
|
||||
|
||||
@@ -24,6 +24,7 @@ PROXY_CONFIG = [
|
||||
'/api/**', '!/api/v1/ws',
|
||||
'!/bisq', '!/bisq/**', '!/bisq/',
|
||||
'!/liquid', '!/liquid/**', '!/liquid/',
|
||||
'!/liquidtestnet', '!/liquidtestnet/**', '!/liquidtestnet/',
|
||||
'/testnet/api/**', '/signet/api/**'
|
||||
],
|
||||
target: "https://mempool.space",
|
||||
@@ -57,6 +58,16 @@ PROXY_CONFIG = [
|
||||
ws: true,
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
},
|
||||
{
|
||||
context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'],
|
||||
target: "https://liquid.network/testnet",
|
||||
pathRewrite: {
|
||||
"^/api/liquidtestnet/": "/liquidtestnet/api"
|
||||
},
|
||||
ws: true,
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { AssetsComponent } from './assets/assets.component';
|
||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
|
||||
import { ApiDocsComponent } from './components/api-docs/api-docs.component';
|
||||
import { DocsComponent } from './components/docs/docs.component';
|
||||
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
|
||||
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
|
||||
@@ -66,9 +66,21 @@ let routes: Routes = [
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
component: ApiDocsComponent,
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'terms-of-service',
|
||||
@@ -93,79 +105,6 @@ let routes: Routes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'liquid',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
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: 'tv',
|
||||
component: TelevisionComponent
|
||||
},
|
||||
{
|
||||
path: 'status',
|
||||
component: StatusViewComponent
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: ''
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'testnet',
|
||||
children: [
|
||||
@@ -212,9 +151,21 @@ let routes: Routes = [
|
||||
children: [],
|
||||
component: AddressComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
component: ApiDocsComponent,
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -278,9 +229,21 @@ let routes: Routes = [
|
||||
children: [],
|
||||
component: AddressComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
component: ApiDocsComponent,
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -298,11 +261,6 @@ let routes: Routes = [
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'bisq',
|
||||
component: MasterPageComponent,
|
||||
loadChildren: () => import('./bisq/bisq.module').then(m => m.BisqModule)
|
||||
},
|
||||
{
|
||||
path: 'tv',
|
||||
component: TelevisionComponent,
|
||||
@@ -380,9 +338,21 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
path: 'assets',
|
||||
component: AssetsComponent,
|
||||
},
|
||||
{
|
||||
path: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
component: ApiDocsComponent,
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
@@ -406,6 +376,107 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'testnet',
|
||||
children: [
|
||||
{
|
||||
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: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
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: 'tv',
|
||||
component: TelevisionComponent
|
||||
@@ -429,3 +500,4 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
|
||||
|
||||
@@ -48,16 +48,20 @@ import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||
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';
|
||||
import { ApiDocsComponent } from './components/api-docs/api-docs.component';
|
||||
import { CodeTemplateComponent } from './components/api-docs/code-template.component';
|
||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ApiDocsComponent } from './components/docs/api-docs.component';
|
||||
import { DocsComponent } from './components/docs/docs.component';
|
||||
import { ApiDocsNavComponent } from './components/docs/api-docs-nav.component';
|
||||
import { CodeTemplateComponent } from './components/docs/code-template.component';
|
||||
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
|
||||
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
|
||||
import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component';
|
||||
import { StorageService } from './services/storage.service';
|
||||
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
|
||||
import { LanguageService } from './services/language.service';
|
||||
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -100,6 +104,8 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
|
||||
TrademarkPolicyComponent,
|
||||
SponsorComponent,
|
||||
PushTransactionComponent,
|
||||
DocsComponent,
|
||||
ApiDocsNavComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
@@ -109,6 +115,7 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
|
||||
BrowserAnimationsModule,
|
||||
InfiniteScrollModule,
|
||||
NgbTypeaheadModule,
|
||||
NgbModule,
|
||||
FontAwesomeModule,
|
||||
SharedModule,
|
||||
NgxEchartsModule.forRoot({
|
||||
@@ -122,6 +129,7 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
|
||||
AudioService,
|
||||
SeoService,
|
||||
StorageService,
|
||||
LanguageService,
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
@@ -158,5 +166,7 @@ export class AppModule {
|
||||
library.addIcons(faCaretDown);
|
||||
library.addIcons(faAngleRight);
|
||||
library.addIcons(faAngleLeft);
|
||||
library.addIcons(faBook);
|
||||
library.addIcons(faListUl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { merge, combineLatest, Observable } from 'rxjs';
|
||||
import { AssetExtended } from '../interfaces/electrs.interface';
|
||||
import { SeoService } from '../services/seo.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-assets',
|
||||
@@ -15,7 +16,8 @@ import { SeoService } from '../services/seo.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AssetsComponent implements OnInit {
|
||||
nativeAssetId = environment.nativeAssetId;
|
||||
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
|
||||
|
||||
assets: AssetExtended[];
|
||||
assetsCache: AssetExtended[];
|
||||
searchForm: FormGroup;
|
||||
@@ -34,6 +36,7 @@ export class AssetsComponent implements OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private seoService: SeoService,
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@@ -52,12 +55,22 @@ export class AssetsComponent implements OnInit {
|
||||
take(1),
|
||||
mergeMap(([assets, qp]) => {
|
||||
this.assets = Object.values(assets);
|
||||
// @ts-ignore
|
||||
this.assets.push({
|
||||
name: 'Liquid Bitcoin',
|
||||
ticker: 'L-BTC',
|
||||
asset_id: this.nativeAssetId,
|
||||
});
|
||||
if (this.stateService.network === 'liquid') {
|
||||
// @ts-ignore
|
||||
this.assets.push({
|
||||
name: 'Liquid Bitcoin',
|
||||
ticker: 'L-BTC',
|
||||
asset_id: this.nativeAssetId,
|
||||
});
|
||||
} else if (this.stateService.network === 'liquidtestnet') {
|
||||
// @ts-ignore
|
||||
this.assets.push({
|
||||
name: 'Test Liquid Bitcoin',
|
||||
ticker: 'tL-BTC',
|
||||
asset_id: this.nativeAssetId,
|
||||
});
|
||||
}
|
||||
|
||||
this.assets = this.assets.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
this.assetsCache = this.assets;
|
||||
this.searchForm.get('searchText').enable();
|
||||
|
||||
@@ -105,10 +105,12 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<app-language-selector *ngIf="stateService.env.BASE_MODULE !== 'bisq'"></app-language-selector>
|
||||
<app-language-selector></app-language-selector>
|
||||
|
||||
<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]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@ export class BisqMainDashboardComponent implements OnInit {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.seoService.setTitle(`Markets`);
|
||||
this.seoService.resetTitle();
|
||||
this.websocketService.want(['blocks']);
|
||||
|
||||
this.usdPrice$ = this.stateService.conversions$.asObservable().pipe(
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BisqBlockComponent } from './bisq-block/bisq-block.component';
|
||||
import { BisqBlocksComponent } from './bisq-blocks/bisq-blocks.component';
|
||||
import { BisqAddressComponent } from './bisq-address/bisq-address.component';
|
||||
import { BisqStatsComponent } from './bisq-stats/bisq-stats.component';
|
||||
import { ApiDocsComponent } from '../components/api-docs/api-docs.component';
|
||||
import { DocsComponent } from '../components/docs/docs.component';
|
||||
import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component';
|
||||
import { BisqMarketComponent } from './bisq-market/bisq-market.component';
|
||||
import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component';
|
||||
@@ -60,9 +60,21 @@ const routes: Routes = [
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
},
|
||||
{
|
||||
path: 'docs/api/:type',
|
||||
component: DocsComponent
|
||||
},
|
||||
{
|
||||
path: 'docs/api',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'docs',
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
component: ApiDocsComponent,
|
||||
redirectTo: 'docs/api/rest'
|
||||
},
|
||||
{
|
||||
path: 'terms-of-service',
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<span style="margin-left: auto; margin-right: -20px; margin-bottom: -20px">™</span>
|
||||
<img class="logo" src="./resources/mempool-logo-bigger.png" />
|
||||
<div class="version">
|
||||
v{{ packetJsonVersion }} [{{ frontendGitCommitHash }}]
|
||||
v{{ packetJsonVersion }} [<a href="https://github.com/mempool/mempool/commit/{{ frontendGitCommitHash }}">{{ frontendGitCommitHash }}</a>]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
<a target="_blank" href="https://twitter.com/mempool">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="svg-inline--fa fa-twitter fa-w-16 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>
|
||||
</a>
|
||||
<a target="_blank" href="https://t.me/mempoolspace">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="telegram-plane" class="svg-inline--fa fa-telegram-plane fa-w-14 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z"></path></svg>
|
||||
</a>
|
||||
<a target="_blank" href="https://keybase.io/team/mempool">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="keybase" class="svg-inline--fa fa-keybase fa-w-14 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M286.17 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18zm111.92-147.6c-9.5-14.62-39.37-52.45-87.26-73.71q-9.1-4.06-18.38-7.27a78.43 78.43 0 0 0-47.88-104.13c-12.41-4.1-23.33-6-32.41-5.77-.6-2-1.89-11 9.4-35L198.66 32l-5.48 7.56c-8.69 12.06-16.92 23.55-24.34 34.89a51 51 0 0 0-8.29-1.25c-41.53-2.45-39-2.33-41.06-2.33-50.61 0-50.75 52.12-50.75 45.88l-2.36 36.68c-1.61 27 19.75 50.21 47.63 51.85l8.93.54a214 214 0 0 0-46.29 35.54C14 304.66 14 374 14 429.77v33.64l23.32-29.8a148.6 148.6 0 0 0 14.56 37.56c5.78 10.13 14.87 9.45 19.64 7.33 4.21-1.87 10-6.92 3.75-20.11a178.29 178.29 0 0 1-15.76-53.13l46.82-59.83-24.66 74.11c58.23-42.4 157.38-61.76 236.25-38.59 34.2 10.05 67.45.69 84.74-23.84.72-1 1.2-2.16 1.85-3.22a156.09 156.09 0 0 1 2.8 28.43c0 23.3-3.69 52.93-14.88 81.64-2.52 6.46 1.76 14.5 8.6 15.74 7.42 1.57 15.33-3.1 18.37-11.15C429 443 434 414 434 382.32c0-38.58-13-77.46-35.91-110.92zM142.37 128.58l-15.7-.93-1.39 21.79 13.13.78a93 93 0 0 0 .32 19.57l-22.38-1.34a12.28 12.28 0 0 1-11.76-12.79L107 119c1-12.17 13.87-11.27 13.26-11.32l29.11 1.73a144.35 144.35 0 0 0-7 19.17zm148.42 172.18a10.51 10.51 0 0 1-14.35-1.39l-9.68-11.49-34.42 27a8.09 8.09 0 0 1-11.13-1.08l-15.78-18.64a7.38 7.38 0 0 1 1.34-10.34l34.57-27.18-14.14-16.74-17.09 13.45a7.75 7.75 0 0 1-10.59-1s-3.72-4.42-3.8-4.53a7.38 7.38 0 0 1 1.37-10.34L214 225.19s-18.51-22-18.6-22.14a9.56 9.56 0 0 1 1.74-13.42 10.38 10.38 0 0 1 14.3 1.37l81.09 96.32a9.58 9.58 0 0 1-1.74 13.44zM187.44 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18z"></path></svg>
|
||||
</a>
|
||||
<a target="_blank" href="https://matrix.to/#/#mempool:bitcoin.kyoto">
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="matrix" class="svg-inline--fa fa-matrix fa-w-16 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1536 1792"><path fill="currentColor" d="M40.467 163.152v1465.696H145.92V1664H0V128h145.92v35.152zm450.757 464.64v74.14h2.069c19.79-28.356 43.717-50.215 71.483-65.575 27.765-15.656 59.963-23.336 96-23.336 34.56 0 66.165 6.795 94.818 20.086 28.652 13.293 50.216 37.22 65.28 70.893 16.246-23.926 38.4-45.194 66.166-63.507 27.766-18.314 60.848-27.472 98.954-27.472 28.948 0 55.828 3.545 80.64 10.635 24.812 7.088 45.785 18.314 63.508 33.968 17.722 15.656 31.31 35.742 41.354 60.85 9.747 25.107 14.768 55.236 14.768 90.683v366.573h-150.35V865.28c0-18.314-.59-35.741-2.068-51.987-1.476-16.247-5.316-30.426-11.52-42.24-6.499-12.112-15.656-21.563-28.062-28.653-12.405-7.088-29.242-10.634-50.214-10.634-21.268 0-38.4 4.135-51.397 12.112-12.997 8.27-23.336 18.608-30.72 31.901-7.386 12.997-12.407 27.765-14.77 44.602-2.363 16.542-3.84 33.379-3.84 50.216v305.133H692.971v-307.2c0-16.247-.294-32.197-1.18-48.149-.591-15.95-3.84-30.424-9.157-44.011-5.317-13.293-14.178-24.223-26.585-32.197-12.406-7.976-30.425-12.112-54.646-12.112-7.088 0-16.542 1.478-28.062 4.726-11.52 3.25-23.04 9.157-33.968 18.02-10.93 8.86-20.383 21.563-28.063 38.103-7.68 16.543-11.52 38.4-11.52 65.28v317.834H349.44V627.792zm1004.309 1001.056V163.152H1390.08V128H1536v1536h-145.92v-35.152z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="enterprise-sponsor">
|
||||
@@ -54,6 +54,10 @@
|
||||
<img class="image" src="/resources/profile/unchained.svg" />
|
||||
<span>Unchained</span>
|
||||
</a>
|
||||
<a href="https://blockstream.com/" target="_blank" title="Blockstream">
|
||||
<img class="image" src="/resources/profile/blockstream.svg" />
|
||||
<span>Blockstream</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -98,6 +102,10 @@
|
||||
<img class="image" src="/resources/profile/ronindojo.png" />
|
||||
<span>RoninDojo</span>
|
||||
</a>
|
||||
<a href="https://github.com/runcitadel/dashboard" target="_blank" title="Citadel">
|
||||
<img class="image" src="/resources/profile/runcitadel.svg" />
|
||||
<span>Citadel</span>
|
||||
</a>
|
||||
<a href="https://github.com/spesmilo/electrum" target="_blank" title="Electrum Wallet">
|
||||
<img class="image" src="/resources/profile/electrum.jpg" />
|
||||
<span>Electrum</span>
|
||||
@@ -138,10 +146,6 @@
|
||||
<img class="image" src="/resources/profile/satpile.jpg" />
|
||||
<span>Satpile</span>
|
||||
</a>
|
||||
<a href="https://github.com/btcontract/lnwallet" target="_blank" title="Bitcoin Lightning Wallet">
|
||||
<img class="image" src="/resources/profile/blw.png" />
|
||||
<span>BLW</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -160,19 +164,45 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contributors">
|
||||
<h3 i18n="about.contributors">Project Contributors</h3>
|
||||
<div class="wrapper">
|
||||
<ng-container *ngIf="contributors$ | async as contributors; else loadingSponsors">
|
||||
<ng-template ngFor let-contributor [ngForOf]="contributors">
|
||||
<ng-container *ngIf="translators$ | async | keyvalue as translators else loadingSponsors">
|
||||
<div class="community-sponsor">
|
||||
<h3 i18n="about.translators">Project Translators</h3>
|
||||
<div class="wrapper">
|
||||
<ng-template ngFor let-translator [ngForOf]="translators">
|
||||
<a [href]="'https://twitter.com/' + translator.value" target="_blank" [title]="translator.key">
|
||||
<img class="image" [src]="'/api/v1/translators/images/' + translator.value" />
|
||||
</a>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="allContributors$ | async as contributors else loadingSponsors">
|
||||
<div class="contributors">
|
||||
<h3 i18n="about.contributors">Project Contributors</h3>
|
||||
<div class="wrapper">
|
||||
<ng-template ngFor let-contributor [ngForOf]="contributors.regular">
|
||||
<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>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="maintainers" *ngIf="contributors.core.length">
|
||||
<h3 i18n="about.project_members">Project Members</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">
|
||||
<h3 i18n="about.maintainers">Project Maintainers</h3>
|
||||
@@ -224,7 +254,7 @@
|
||||
</div>
|
||||
|
||||
<div class="footer-version" *ngIf="officialMempoolSpace">
|
||||
{{ (backendInfo$ | async)?.hostname }} (v{{ (backendInfo$ | async )?.version }}) [{{ (backendInfo$ | async )?.gitCommit | slice:0:8 }}]
|
||||
{{ (backendInfo$ | async)?.hostname }} (v{{ (backendInfo$ | async )?.version }}) [<a href="https://github.com/mempool/mempool/commit/{{ (backendInfo$ | async )?.gitCommit | slice:0:8 }}">{{ (backendInfo$ | async )?.gitCommit | slice:0:8 }}</a>]
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
img {
|
||||
box-shadow: 0px 0px 20px #1bd8f4;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
img, span{
|
||||
@@ -180,4 +180,4 @@
|
||||
|
||||
.no-about-margin {
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { Observable } from 'rxjs';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { IBackendInfo } from 'src/app/interfaces/websocket.interface';
|
||||
import { Router } from '@angular/router';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ITranslators } from 'src/app/interfaces/node-api.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-about',
|
||||
@@ -16,7 +18,8 @@ import { Router } from '@angular/router';
|
||||
export class AboutComponent implements OnInit {
|
||||
backendInfo$: Observable<IBackendInfo>;
|
||||
sponsors$: Observable<any>;
|
||||
contributors$: Observable<any>;
|
||||
translators$: Observable<ITranslators>;
|
||||
allContributors$: Observable<any>;
|
||||
frontendGitCommitHash = this.stateService.env.GIT_COMMIT_HASH;
|
||||
packetJsonVersion = this.stateService.env.PACKAGE_JSON_VERSION;
|
||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||
@@ -37,7 +40,25 @@ export class AboutComponent implements OnInit {
|
||||
this.websocketService.want(['blocks']);
|
||||
|
||||
this.sponsors$ = this.apiService.getDonation$();
|
||||
this.contributors$ = this.apiService.getContributor$();
|
||||
this.translators$ = this.apiService.getTranslators$()
|
||||
.pipe(
|
||||
map((translators) => {
|
||||
for (const t in translators) {
|
||||
if (translators[t] === '') {
|
||||
delete translators[t]
|
||||
}
|
||||
}
|
||||
return translators;
|
||||
})
|
||||
);
|
||||
this.allContributors$ = this.apiService.getContributor$().pipe(
|
||||
map((contributors) => {
|
||||
return {
|
||||
regular: contributors.filter((user) => !user.core_constributor),
|
||||
core: contributors.filter((user) => user.core_constributor),
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sponsor() {
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
<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>
|
||||
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">
|
||||
<span class="d-inline d-lg-none">{{ addressInfo.unconfidential | shortenString : 14 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressInfo.unconfidential }}</span>
|
||||
</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></td>
|
||||
</tr>
|
||||
<ng-template [ngIf]="!address.electrum">
|
||||
<tr>
|
||||
|
||||
@@ -99,7 +99,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
.pipe(
|
||||
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)) {
|
||||
if ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([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;
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
<span class="fiat">{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-container>
|
||||
<ng-template #viewFiatVin>
|
||||
<ng-template [ngIf]="network === 'liquid' && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
|
||||
<ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
|
||||
<span i18n="shared.confidential">Confidential</span>
|
||||
</ng-template>
|
||||
<ng-template #default>
|
||||
‎{{ satoshis / 100000000 | number : digitsInfo }}
|
||||
<span class="symbol"><ng-template [ngIf]="network === 'liquid'">L-</ng-template>
|
||||
<ng-template [ngIf]="network === 'liquidtestnet'">tL-</ng-template>
|
||||
<ng-template [ngIf]="network === 'testnet'">t</ng-template>
|
||||
<ng-template [ngIf]="network === 'signet'">s</ng-template>BTC</span>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,899 +0,0 @@
|
||||
<ng-container *ngIf="{ val: network$ | async } as network">
|
||||
<div class="container-xl text-left">
|
||||
<div class="text-center">
|
||||
<h2>{{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API Service</ng-container></h2>
|
||||
</div>
|
||||
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
|
||||
<li *ngIf="network.val !== 'bisq' && network.val !== 'liquid'" [ngbNavItem]="0">
|
||||
<a ngbNavLink i18n="api-docs.tab.general|API Docs tab for General">General</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark">
|
||||
<ngb-panel id="difficultyAdjustment" *ngIf="network.val !== 'liquid'">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Difficulty Adjustment</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="difficulty">
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.difficulty)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/difficulty-adjustment</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about difficulty adjustment.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.difficulty" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val === 'bisq'" [ngbNavItem]="0">
|
||||
<a ngbNavLink i18n="Bisq All Markets">Markets</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="bisqMarketsCurrencies">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Market Currencies</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketsCurrencies)" target="_blank">GET {{ baseNetworkUrl }}/api/currencies</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of available currencies for a given base currency. </div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketsCurrencies" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="marketDepth">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Market Depth</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketDepth)" target="_blank">GET {{ baseNetworkUrl }}/api/depth?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of open offer prices for a single market.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketDepth" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="bisqMarketsHloc">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Market HLOC</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketHloc)" target="_blank">GET {{ baseNetworkUrl }}/api/hloc?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides hi/low/open/close data for a given market. This can be used to generate a candlestick chart.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketHloc" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="bisqMarketsMarkets">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Markets</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.markets)" target="_blank">GET {{ baseNetworkUrl }}/api/markets</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of available markets.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.markets" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="bisqMarketsOffers">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Market Offers</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketOffers)" target="_blank">GET {{ baseNetworkUrl }}/api/offers?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of open offer details for a single market.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketOffers" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="bisqMarketsTicker">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Market Ticker</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketTicker)" target="_blank">GET {{ baseNetworkUrl }}/api/ticker?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides 24 hour price ticker for single market or all markets</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketTicker" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="bisqMarketsTrades">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Market Trades</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketTrades)" target="_blank">GET {{ baseNetworkUrl }}/api/trades?market=[:market]&limit=[:limit]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of completed trades for a single market.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketTrades" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="bisqMarketsVolumes">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Market Volumes</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketVolumes)" target="_blank">GET {{ baseNetworkUrl }}/api/volumes?basecurrency=[:basecurrency]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides periodic volume data in terms of base currency for one or all markets.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketVolumes" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val === 'bisq'" [ngbNavItem]="1">
|
||||
<a ngbNavLink i18n="api-docs.tab.bsq|API Docs tab for BSQ">General</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="bisqStats">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Stats</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.stats)" target="_blank'" target="_blank">GET {{ baseNetworkUrl }}/api/stats</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns statistics about all Bisq transactions.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.stats" [network]="network.val"></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="2">
|
||||
<a ngbNavLink i18n="api-docs.tab.addresses|API Docs tab for Addresses">Addresses</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
<ngb-panel id="address">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Address</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.address)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about an address. Available fields: <code>address</code>, <code>chain_stats</code>, and <code>mempool_stats</code>. {{ '{' }}chain,mempool{{ '}' }}_stats each contain an object with <code>tx_count</code>, <code>funded_txo_count</code>, <code>funded_txo_sum</code>, <code>spent_txo_count</code>, and <code>spent_txo_sum</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.address" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressTransactions">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Address Transactions</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressTransactions)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using <code>:last_seen_txid</code> (see below).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressTransactions" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressTransactionsChain">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Address Transactions Chain</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressTransactionsChain)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/chain</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get confirmed transaction history for the specified address/scripthash, sorted with newest first. Returns 25 transactions per page. More can be requested by specifying the last txid seen by the previous query.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressTransactionsChain" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressTransactionsMempool">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Address Transactions Mempool</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressTransactionsMempool)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/mempool</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressTransactionsMempool" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressUTXO">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Address UTXO</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressUTXO)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/utxo</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressUTXO" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val === 'liquid'" [ngbNavItem]="3">
|
||||
<a ngbNavLink i18n="api-docs.tab.assets|API Docs tab for Assets">Assets</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="assets">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Assets</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.assets)" target="_blank">GET /liquid/api/asset/:asset_id</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns information about a Liquid asset.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assets" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="assetTransactions">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Asset Transactions</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<a [href]="wrapUrl(network.val, code.assetTransactions)" target="_blank">GET /liquid/api/asset/:asset_id/txs[/mempool|/chain]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns transactions associated with the specified Liquid asset. For the network's native asset, returns a list of peg in, peg out, and burn transactions. For user-issued assets, returns a list of issuance, reissuance, and burn transactions. Does not include regular transactions transferring this asset.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assetTransactions" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="assetSupply">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Asset Supply</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.assetSupply)" target="_blank">GET /liquid/api/asset/:asset_id/supply[/decimal]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get the current total supply of the specified asset. For the native asset (L-BTC), this is calculated as [chain,mempool]_stats.peg_in_amount - [chain,mempool]_stats.peg_out_amount - [chain,mempool]_stats.burned_amount. For issued assets, this is calculated as [chain,mempool]_stats.issued_amount - [chain,mempool]_stats.burned_amount. Not available for assets with blinded issuances. If /decimal is specified, returns the supply as a decimal according to the asset's divisibility. Otherwise, returned in base units.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assetSupply" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="4">
|
||||
<a ngbNavLink i18n="api-docs.tab.blocks|API Docs tab for Blocks">Blocks</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="block">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.block)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.block" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockHeader">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Header</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockHeader)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/header</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the hex-encoded block header.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockHeader" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockHeight">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Height</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockHeader)" target="_blank">GET {{ baseNetworkUrl }}/api/block-height/:height</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the hash of the block currently at <code>:height</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockHeight" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockRaw">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Raw</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<a [href]="wrapUrl(network.val, code.blockRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/raw</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the raw block representation in binary.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockRaw" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockStatus">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Status</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="title">Get Block Status</div>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/status</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the confirmation status of a block. Available fields: <code>in_best_chain</code> (boolean, false for orphaned blocks), <code>next_best</code> (the hash of the next block, only available for blocks in the best chain).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockStatus" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="blockTipHeight">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Tip Height</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTipHeight)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/height</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the height of the last block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTipHeight" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTipHash">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Tip Hash</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTipHash)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/hash</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the hash of the last block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTipHash" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTxId">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Transaction ID</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTxId)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txid/:index</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the transaction at index <code>:index</code> within the specified block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTxId" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTxIds">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Transaction IDs</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTxIds)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txids</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a list of all txids in the block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTxIds" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTxs">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Block Transactions</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txs[/:start_index]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a list of transactions in the block (up to 25 transactions beginning at <code>start_index</code>). Transactions returned here do not have the <code>status</code> field, since all the transactions share the same block and confirmation status.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTxs" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="blocks">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Blocks</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blocks)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks[/:start_height]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blocks" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val === 'bisq'" id="blocks">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Blocks</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blocksBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/:index/:length</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blocksBisq" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="5">
|
||||
<a ngbNavLink i18n="api-docs.tab.fees|API Docs tab for Fees">Fees</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark">
|
||||
|
||||
<ngb-panel id="feeMempoolBlocks">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Mempool Blocks Fees</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.feeMempoolBlocks)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/mempool-blocks</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.fees.mempool-blocks|API Docs for /api/v1/fees/mempool-blocks">Returns current mempool as projected blocks.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.feeMempoolBlocks" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="feeRecommended">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Recommended Fees</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.feeRecommended)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/recommended</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.fees.recommended|API Docs for /api/v1/fees/recommended">Returns our currently suggested fees for new transactions.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.feeRecommended" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="6">
|
||||
<a ngbNavLink i18n="api-docs.tab.mempool|API Docs tab for Mempool">Mempool</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="mempool">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Mempool</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.mempool)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.mempool.mempool|API Docs for /api/mempool">Returns current mempool backlog statistics.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.mempool" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="mempoolTxs">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Mempool Transactions IDs</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.mempoolTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/txids</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.mempool.txids|API Docs for /api/mempool/txids">Get the full list of txids in the mempool as an array. The order of the txids is arbitrary and does not match bitcoind.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.mempoolTxs" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="mempoolRecent">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Mempool Recent</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.mempoolRecent)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/recent</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.mempool.recent|API Docs for /api/mempool/recent">Get a list of the last 10 transactions to enter the mempool. Each transaction object contains simplified overview data, with the following fields: <code>txid</code>, <code>fee</code>, <code>vsize</code>, and <code>value</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.mempoolRecent" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="7">
|
||||
<a ngbNavLink i18n="api-docs.tab.transactions|API Docs tab for Transactions">Transactions</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="cpfp">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Children Pay for Parent</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionCpfp)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/cpfp/:txid</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.fees.cpfp|API Docs for /api/v1/fees/cpfp">Returns the ancestors and the best descendant fees for a transaction.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionCpfp" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel id="transaction">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<a [href]="wrapUrl(network.val, code.transaction)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about a transaction. Available fields: <code>txid</code>, <code>version</code>, <code>locktime</code>, <code>size</code>, <code>weight</code>, <code>fee</code>, <code>vin</code>, <code>vout</code>, and <code>status</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transaction" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionHex">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction Hex</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionHex)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/hex</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a transaction serialized as hex.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionHex" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq' && network.val !== 'liquid'" id="transactionMerkleBlockProof">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction Merkleblock Proof</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionMerkleBlockProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkleblock-proof</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionMerkleBlockProof" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionMerkleProof">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction Merkle Proof</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionMerkleProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkle-proof</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a merkle inclusion proof for the transaction using <a href="https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-transaction-get-merkle">Electrum's blockchain.transaction.get_merkle format.</a></div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionMerkleProof" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionOutspend">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction Outspend</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionOutspend)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspend/:vout</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the spending status of a transaction output. Available fields: <code>spent</code> (boolean), <code>txid</code> (optional), <code>vin</code> (optional), and <code>status</code> (optional, the status of the spending tx).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionOutspend" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionOutspends">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction Outspends</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionOutspends)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspends</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the spending status of all transaction outputs.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionOutspends" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionRaw">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction Raw</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/raw</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a transaction as binary data.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionRaw" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionStatus">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transaction Status</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/status</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the confirmation status of a transaction. Available fields: <code>confirmed</code> (boolean), <code>block_height</code> (optional), and <code>block_hash</code> (optional).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionStatus" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val === 'bisq'" id="transactionsBisq">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>GET Transactions</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="title">Get Mempool Txids</div>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionsBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/txs/:index/:length</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns :length of latest Bisq transactions, starting from :index.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionsBisq" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="postTransaction">
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>POST Transaction</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<div>POST /api/tx</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The <code>txid</code> will be returned on success.</div>
|
||||
</div>
|
||||
<app-code-template [method]="'post'" [hostname]="hostname" [code]="code.transactionPost" [network]="network.val" ></app-code-template>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
|
||||
</ngb-accordion>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="8">
|
||||
<a ngbNavLink i18n="api-docs.tab.websocket|API Docs tab for Websocket">Websocket</a>
|
||||
<ng-template ngbNavContent >
|
||||
<div class="websocket">
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
{{ wrapUrl(network.val, code.websocket, true) }}
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.websocket.websocket">Default push: <code>{{ '{' }} action: 'want', data: ['blocks', ...] {{ '}' }}</code> to express what you want pushed. Available: <code>blocks</code>, <code>mempool-blocks</code>, <code>live-2h-chart</code>, and <code>stats</code>.<br><br>Push transactions related to address: <code>{{ '{' }} 'track-address': '3PbJ...bF9B' {{ '}' }}</code> to receive all new transactions containing that address as input or output. Returns an array of transactions. <code>address-transactions</code> for new mempool transactions, and <code>block-transactions</code> for new block confirmed transactions.</div>
|
||||
</div>
|
||||
<app-code-template [method]="'websocket'" [hostname]="hostname" [code]="code.websocket" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="text-center">
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
|
|
||||
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -1,76 +0,0 @@
|
||||
.text-small {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #1d1f31;
|
||||
font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New;
|
||||
}
|
||||
|
||||
tr {
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
li.nav-item {
|
||||
width: 100%;
|
||||
@media (min-width: 676px){
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
border-bottom: 1px solid #fff;
|
||||
@media (min-width: 676px){
|
||||
border-bottom: 1px solid #11131f;
|
||||
}
|
||||
}
|
||||
|
||||
.code-tab {
|
||||
width: auto;
|
||||
margin: 20px auto 10px;
|
||||
li.nav-item {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.code {
|
||||
.tab-content {
|
||||
padding: 0px;
|
||||
}
|
||||
.nav-tabs .nav-link.active {
|
||||
border-bottom: 1px solid #11131f;
|
||||
}
|
||||
.subtitle {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.description {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.title {
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.subtitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
.divider {
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
height: 1px;
|
||||
background: #333;
|
||||
}
|
||||
.websocket {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.difficulty {
|
||||
padding: 15px;
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
<td i18n="asset.issuer|Liquid Asset issuer">Issuer</td>
|
||||
<td><a target="_blank" href="{{ 'http://' + assetContract[0] }}">{{ assetContract[0] }}</a></td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<tr *ngIf="asset.issuance_txin">
|
||||
<td i18n="asset.issuance-tx|Liquid Asset issuance TX">Issuance TX</td>
|
||||
<td><a [routerLink]="['/tx/' | relativeUrl, asset.issuance_txin.txid]">{{ asset.issuance_txin.txid | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.issuance_txin.txid"></app-clipboard></td>
|
||||
</tr>
|
||||
@@ -42,15 +42,15 @@
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_in_amount">
|
||||
<td i18n="asset.pegged-in|Liquid Asset pegged-in amount">Pegged in</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_in_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_out_amount">
|
||||
<td i18n="asset.pegged-out|Liquid Asset pegged-out amount">Pegged out</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<tr *ngIf="asset.chain_stats.issued_amount">
|
||||
<td i18n="asset.issued-amount|Liquid Asset issued amount">Issued amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
@@ -58,11 +58,11 @@
|
||||
<td i18n="asset.burned-amount|Liquid Asset burned amount">Burned amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<tr *ngIf="asset.chain_stats.issued_amount">
|
||||
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount - asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_in_amount">
|
||||
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_in_amount - asset.chain_stats.burned_amount - asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
@@ -78,7 +78,7 @@
|
||||
<div class="title-tx">
|
||||
<h2>
|
||||
<ng-template [ngIf]="transactions?.length" i18n="asset.M_of_N">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} </ng-template>
|
||||
<ng-template [ngIf]="isNativeAsset" [ngIfElse]="defaultAsset" i18n="Liquid native asset transactions title">Peg In/Out and Burn Transactions</ng-template>
|
||||
<ng-template [ngIf]="isNativeAsset && network === 'liquid'" [ngIfElse]="defaultAsset" i18n="Liquid native asset transactions title">Peg In/Out and Burn Transactions</ng-template>
|
||||
<ng-template #defaultAsset i18n="Default asset transactions title">Issuance and Burn Transactions</ng-template>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { moveDec } from 'src/app/bitcoin.utils';
|
||||
})
|
||||
export class AssetComponent implements OnInit, OnDestroy {
|
||||
network = '';
|
||||
nativeAssetId = environment.nativeAssetId;
|
||||
nativeAssetId = this.stateService.network === 'liquidtestnet' ? environment.nativeTestAssetId : environment.nativeAssetId;
|
||||
|
||||
asset: Asset;
|
||||
blindedIssuance: boolean;
|
||||
|
||||
@@ -10,17 +10,18 @@
|
||||
</ng-container>
|
||||
</a>
|
||||
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED || env.LIQUID_TESTNET_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>
|
||||
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage" ngbDropdownItem class="mainnet"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1"> Mainnet</a>
|
||||
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + '/signet'" ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet"><img src="./resources/signet-logo.png" style="width: 30px;" class="mr-1"> Signet</a>
|
||||
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + '/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>
|
||||
<a [href]="env.LIQUID_WEBSITE_URL + urlLanguage" ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a>
|
||||
<a [href]="env.LIQUID_WEBSITE_URL + urlLanguage + '/testnet'" ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,8 +39,8 @@
|
||||
<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">
|
||||
<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 class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/docs']" (click)="collapse()"><fa-icon [icon]="['fas', 'book']" [fixedWidth]="true" i18n-title="master-page.docs" title="Docs"></fa-icon></a>
|
||||
</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>
|
||||
|
||||
@@ -102,6 +102,10 @@ nav {
|
||||
background-color: #116761;
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Env, StateService } from '../../services/state.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { LanguageService } from 'src/app/services/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bisq-master-page',
|
||||
@@ -12,14 +13,17 @@ export class BisqMasterPageComponent implements OnInit {
|
||||
navCollapsed = false;
|
||||
env: Env;
|
||||
isMobile = window.innerWidth <= 767.98;
|
||||
|
||||
urlLanguage: string;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private languageService: LanguageService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.env = this.stateService.env;
|
||||
this.connectionState$ = this.stateService.connectionState$;
|
||||
this.urlLanguage = this.languageService.getLanguageForUrl();
|
||||
}
|
||||
|
||||
collapse(): void {
|
||||
|
||||
@@ -81,12 +81,12 @@
|
||||
<ng-template [ngIf]="fees !== undefined" [ngIfElse]="loadingFees">
|
||||
<tr>
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td *ngIf="network !== 'liquid'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat></span></td>
|
||||
<td *ngIf="network !== 'liquid' && network !== 'liquidtestnet'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat></span></td>
|
||||
<ng-template #liquidTotalFees>
|
||||
<td>{{ fees * 100000000 | number }} L-sat (<app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat>)</td>
|
||||
<td><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat></td>
|
||||
</ng-template>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
|
||||
<td>
|
||||
<app-amount [satoshis]="(blockSubsidy + fees) * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat></span>
|
||||
@@ -98,7 +98,7 @@
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td style="width: 75%;"><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
@@ -125,7 +125,7 @@
|
||||
<td class="td-width" i18n="transaction.version">Version</td>
|
||||
<td>{{ block.version | decimal2hex }} <span *ngIf="displayTaprootStatus() && hasTaproot(block.version)" class="badge badge-success ml-1" >Taproot</span></td>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<td i18n="block.bits">Bits</td>
|
||||
<td>{{ block.bits | decimal2hex }}</td>
|
||||
</tr>
|
||||
@@ -136,7 +136,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm" *ngIf="network !== 'liquid'">
|
||||
<div class="col-sm" *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Observable, of, Subscription } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block',
|
||||
@@ -51,6 +52,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
private stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
private websocketService: WebsocketService,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@@ -194,7 +196,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
if (this.showNextBlocklink) {
|
||||
this.navigateToNextBlock();
|
||||
} else {
|
||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/mempool-block/', '0']);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/mempool-block'), '0']);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -210,7 +212,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
setBlockSubsidy() {
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
this.blockSubsidy = 0;
|
||||
return;
|
||||
}
|
||||
@@ -277,13 +279,13 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
const block = this.latestBlocks.find((b) => b.height === this.nextBlockHeight - 2);
|
||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/block/',
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/block/'),
|
||||
block ? block.id : this.block.previousblockhash], { state: { data: { block, blockHeight: this.nextBlockHeight - 2 } } });
|
||||
}
|
||||
|
||||
navigateToNextBlock() {
|
||||
const block = this.latestBlocks.find((b) => b.height === this.nextBlockHeight);
|
||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + '/block/',
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/block/'),
|
||||
block ? block.id : this.nextBlockHeight], { state: { data: { block, blockHeight: this.nextBlockHeight } } });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<div class="blocks-container blockchain-blocks-container" *ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate">
|
||||
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
|
||||
<div class="text-center bitcoin-block mined-block blockchain-blocks-{{ i }}" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]" [class.blink-bg]="(specialBlocks[block.height] !== undefined)">
|
||||
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink"> </a>
|
||||
<a draggable="false" [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }"
|
||||
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a>
|
||||
<div class="block-height">
|
||||
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.blockLink.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mined-block {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
|
||||
@@ -36,18 +36,19 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
'': ['#9339f4', '#105fb0'],
|
||||
bisq: ['#9339f4', '#105fb0'],
|
||||
liquid: ['#116761', '#183550'],
|
||||
'liquidtestnet': ['#494a4a', '#272e46'],
|
||||
testnet: ['#1d486f', '#183550'],
|
||||
signet: ['#6f1d5d', '#471850'],
|
||||
};
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
public stateService: StateService,
|
||||
private router: Router,
|
||||
private cd: ChangeDetectorRef,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
|
||||
this.feeRounding = '1.0-1';
|
||||
}
|
||||
this.emptyBlocks.forEach((b) => this.emptyBlockStyles.push(this.getStyleForEmptyBlock(b)));
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
.blockchain-wrapper {
|
||||
overflow: hidden;
|
||||
height: 250px;
|
||||
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+/Edge */
|
||||
user-select: none; /* Standard */
|
||||
}
|
||||
|
||||
.position-container {
|
||||
@@ -26,7 +31,7 @@
|
||||
top: 75px;
|
||||
}
|
||||
|
||||
.position-container.liquid {
|
||||
.position-container.liquid, .position-container.liquidtestnet {
|
||||
left: 420px;
|
||||
}
|
||||
|
||||
@@ -34,7 +39,7 @@
|
||||
.position-container {
|
||||
left: 95%;
|
||||
}
|
||||
.position-container.liquid {
|
||||
.position-container.liquid, .position-container.liquidtestnet {
|
||||
left: 50%;
|
||||
}
|
||||
.position-container.loading {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.btn-link {
|
||||
padding: 0.25rem 0 0.1rem 0.5rem;
|
||||
}
|
||||
|
||||
76
frontend/src/app/components/docs/api-docs-nav.component.html
Normal file
76
frontend/src/app/components/docs/api-docs-nav.component.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<ng-template [ngIf]="network.val !== 'bisq' && network.val !== 'liquid' && network.val !== 'liquidtestnet'">
|
||||
<p>General</p>
|
||||
<a [routerLink]="['./']" fragment="get-difficulty-adjustment" (click)="collapseItem.toggle()">GET Difficulty Adjustment</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="network.val === 'bisq'">
|
||||
<p>Markets</p>
|
||||
<a [routerLink]="['./']" fragment="get-market-currencies" (click)="collapseItem.toggle()">GET Market Currencies</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-depth" (click)="collapseItem.toggle()">GET Market Depth</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-hloc" (click)="collapseItem.toggle()">GET Market HLOC</a>
|
||||
<a [routerLink]="['./']" fragment="get-markets" (click)="collapseItem.toggle()">GET Markets</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-offers" (click)="collapseItem.toggle()">GET Market Offers</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-ticker" (click)="collapseItem.toggle()">GET Market Ticker</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-trades" (click)="collapseItem.toggle()">GET Market Trades</a>
|
||||
<a [routerLink]="['./']" fragment="get-market-volumes" (click)="collapseItem.toggle()">GET Market Volumes</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="network.val === 'bisq'">
|
||||
<p>General</p>
|
||||
<a [routerLink]="['./']" fragment="get-stats" (click)="collapseItem.toggle()">GET Stats</a>
|
||||
</ng-template>
|
||||
|
||||
<p>Addresses</p>
|
||||
<a [routerLink]="['./']" fragment="get-address" (click)="collapseItem.toggle()">GET Address</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions" (click)="collapseItem.toggle()">GET Address Transactions</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions-chain" (click)="collapseItem.toggle()">GET Address Transactions Chain</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions-mempool" (click)="collapseItem.toggle()">GET Address Transactions Mempool</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-utxo" (click)="collapseItem.toggle()">GET Address UTXO</a>
|
||||
|
||||
<ng-template [ngIf]="network.val === 'liquid' || network.val === 'liquidtestnet'">
|
||||
<p>Assets</p>
|
||||
<a [routerLink]="['./']" fragment="get-asset" (click)="collapseItem.toggle()">GET Asset</a>
|
||||
<a [routerLink]="['./']" fragment="get-asset-transactions" (click)="collapseItem.toggle()">GET Asset Transactions</a>
|
||||
<a [routerLink]="['./']" fragment="get-asset-supply" (click)="collapseItem.toggle()">GET Asset Supply</a>
|
||||
<a [routerLink]="['./']" fragment="get-asset-icons" (click)="collapseItem.toggle()">GET Asset Icons</a>
|
||||
<a [routerLink]="['./']" fragment="get-asset-icon" (click)="collapseItem.toggle()">GET Asset Icon</a>
|
||||
</ng-template>
|
||||
|
||||
<p>Blocks</p>
|
||||
<a [routerLink]="['./']" fragment="get-block" (click)="collapseItem.toggle()">GET Block</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-header" (click)="collapseItem.toggle()">GET Block Header</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-height" (click)="collapseItem.toggle()">GET Block Height</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-raw" (click)="collapseItem.toggle()">GET Block Raw</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-status" (click)="collapseItem.toggle()">GET Block Status</a>
|
||||
<a [routerLink]="['./']" fragment="get-block-tip-height" (click)="collapseItem.toggle()">GET Block Tip Height</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-tip-hash" (click)="collapseItem.toggle()">GET Block Tip Hash</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transaction-id" (click)="collapseItem.toggle()">GET Block Transaction ID</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transaction-ids" (click)="collapseItem.toggle()">GET Block Transaction IDs</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transactions" (click)="collapseItem.toggle()">GET Block Transactions</a>
|
||||
<a [routerLink]="['./']" fragment="get-blocks" (click)="collapseItem.toggle()">GET Blocks</a>
|
||||
|
||||
<ng-template [ngIf]="network.val !== 'bisq'">
|
||||
<p>Fees</p>
|
||||
<a [routerLink]="['./']" fragment="get-mempool-blocks-fees" (click)="collapseItem.toggle()">GET Mempool Blocks Fees</a>
|
||||
<a [routerLink]="['./']" fragment="get-recommended-fees" (click)="collapseItem.toggle()">GET Recommended Fees</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="network.val !== 'bisq'">
|
||||
<p>Mempool</p>
|
||||
<a [routerLink]="['./']" fragment="get-mempool" (click)="collapseItem.toggle()">GET Mempool</a>
|
||||
<a [routerLink]="['./']" fragment="get-mempool-transaction-ids" (click)="collapseItem.toggle()">GET Mempool Transaction IDs</a>
|
||||
<a [routerLink]="['./']" fragment="get-mempool-recent" (click)="collapseItem.toggle()">GET Mempool Recent</a>
|
||||
</ng-template>
|
||||
|
||||
<p>Transactions</p>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-cpfp" (click)="collapseItem.toggle()">GET Children Pay for Parent</a>
|
||||
<a [routerLink]="['./']" fragment="get-transaction" (click)="collapseItem.toggle()">GET Transaction</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-hex" (click)="collapseItem.toggle()">GET Transaction Hex</a>
|
||||
<a *ngIf="network.val !== 'bisq' && network.val !== 'liquid' && network.val !== 'liquidtestnet'" [routerLink]="['./']" fragment="get-transaction-merkleblock-proof" (click)="collapseItem.toggle()">GET Transaction Merkleblock Proof</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-merkle-proof" (click)="collapseItem.toggle()">GET Transaction Merkle Proof</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-outspend" (click)="collapseItem.toggle()">GET Transaction Outspend</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-outspends" (click)="collapseItem.toggle()">GET Transaction Outspends</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-raw" (click)="collapseItem.toggle()">GET Transaction Raw</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-status" (click)="collapseItem.toggle()">GET Transaction Status</a>
|
||||
<a *ngIf="network.val === 'bisq'" [routerLink]="['./']" fragment="get-transactions" (click)="collapseItem.toggle()">GET Transactions</a>
|
||||
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="post-transaction" (click)="collapseItem.toggle()">POST Transaction</a>
|
||||
17
frontend/src/app/components/docs/api-docs-nav.component.scss
Normal file
17
frontend/src/app/components/docs/api-docs-nav.component.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
p {
|
||||
color: #4a68b9;
|
||||
font-weight: 700;
|
||||
margin: 10px 0;
|
||||
margin: 15px 0 10px 0;
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
}
|
||||
18
frontend/src/app/components/docs/api-docs-nav.component.ts
Normal file
18
frontend/src/app/components/docs/api-docs-nav.component.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-api-docs-nav',
|
||||
templateUrl: './api-docs-nav.component.html',
|
||||
styleUrls: ['./api-docs-nav.component.scss']
|
||||
})
|
||||
export class ApiDocsNavComponent implements OnInit {
|
||||
|
||||
@Input() network: any;
|
||||
@Input() collapseItem: any = { toggle: () => {} };
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
705
frontend/src/app/components/docs/api-docs.component.html
Normal file
705
frontend/src/app/components/docs/api-docs.component.html
Normal file
@@ -0,0 +1,705 @@
|
||||
<ng-container *ngIf="{ val: network$ | async } as network">
|
||||
<div class="container-xl text-left">
|
||||
|
||||
<div id="restAPI" *ngIf="restTabActivated">
|
||||
|
||||
<div id="doc-nav-desktop" class="hide-on-mobile" [ngClass]="desktopDocsNavPosition">
|
||||
<app-api-docs-nav [network]="{ val: network$ | async }"></app-api-docs-nav>
|
||||
</div>
|
||||
|
||||
<div class="doc-content">
|
||||
|
||||
<p class="hide-on-mobile no-bottom-space">Reference for the {{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API service</ng-container>.</p>
|
||||
|
||||
<div id="doc-nav-mobile" class="hide-on-desktop" *ngIf="showFloatingDocsNav">
|
||||
<button type="button" class="btn btn-outline-primary" (click)="collapse.toggle()" [attr.aria-expanded]="mobileMenuOpen" aria-controls="collapseExample"><fa-icon [icon]="['fas', 'list-ul']" [fixedWidth]="true"></fa-icon> {{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} REST API Overview</button>
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="mobileMenuOpen">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<app-api-docs-nav [collapseItem]="collapse" [network]="{ val: network$ | async }"></app-api-docs-nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mobile-top-doc-nav" #mobileFixedApiNav class="hide-on-desktop"><app-api-docs-nav [network]="{ val: network$ | async }"></app-api-docs-nav></div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq' && network.val !== 'liquid' && network.val !== 'liquidtestnet'">
|
||||
|
||||
<div class="endpoint-container" id="get-difficulty-adjustment">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-difficulty-adjustment">GET Difficulty Adjustment <span>General</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.difficulty)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/difficulty-adjustment</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about difficulty adjustment.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.difficulty" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val === 'bisq'">
|
||||
|
||||
<div class="endpoint-container" id="get-market-currencies">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-market-currencies">GET Market Currencies <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketsCurrencies)" target="_blank">GET {{ baseNetworkUrl }}/api/currencies</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of available currencies for a given base currency. </div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketsCurrencies" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-market-depth">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-market-depth">GET Market Depth <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketDepth)" target="_blank">GET {{ baseNetworkUrl }}/api/depth?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of open offer prices for a single market.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketDepth" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-market-hloc">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-market-hloc">GET Market HLOC <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketHloc)" target="_blank">GET {{ baseNetworkUrl }}/api/hloc?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides hi/low/open/close data for a given market. This can be used to generate a candlestick chart.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketHloc" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-markets">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-markets">GET Markets <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.markets)" target="_blank">GET {{ baseNetworkUrl }}/api/markets</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of available markets.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.markets" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-market-offers">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-market-offers">GET Market Offers <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketOffers)" target="_blank">GET {{ baseNetworkUrl }}/api/offers?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of open offer details for a single market.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketOffers" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-market-ticker">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-market-ticker">GET Market Ticker <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketTicker)" target="_blank">GET {{ baseNetworkUrl }}/api/ticker?market=[:market]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides 24 hour price ticker for single market or all markets</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketTicker" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-market-trades">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-market-trades">GET Market Trades <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketTrades)" target="_blank">GET {{ baseNetworkUrl }}/api/trades?market=[:market]&limit=[:limit]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides list of completed trades for a single market.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketTrades" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-market-volumes">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-market-volumes">GET Market Volumes <span>Markets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.marketVolumes)" target="_blank">GET {{ baseNetworkUrl }}/api/volumes?basecurrency=[:basecurrency]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Provides periodic volume data in terms of base currency for one or all markets.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.marketVolumes" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val === 'bisq'">
|
||||
|
||||
<div class="endpoint-container" id="get-stats">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-stats">GET Stats <span>General</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.stats)" target="_blank'" target="_blank">GET {{ baseNetworkUrl }}/api/stats</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns statistics about all Bisq transactions.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.stats" [network]="network.val"></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category">
|
||||
|
||||
<div class="endpoint-container" id="get-address">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-address">GET Address <span>Addresses</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.address)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about an address. Available fields: <code>address</code>, <code>chain_stats</code>, and <code>mempool_stats</code>. {{ '{' }}chain,mempool{{ '}' }}_stats each contain an object with <code>tx_count</code>, <code>funded_txo_count</code>, <code>funded_txo_sum</code>, <code>spent_txo_count</code>, and <code>spent_txo_sum</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.address" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-transactions">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-address-transactions">GET Address Transactions <span>Addresses</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressTransactions)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using <code>:last_seen_txid</code> (see below).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressTransactions" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-transactions-chain">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-address-transactions-chain">GET Address Transactions Chain <span>Addresses</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressTransactionsChain)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/chain</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get confirmed transaction history for the specified address/scripthash, sorted with newest first. Returns 25 transactions per page. More can be requested by specifying the last txid seen by the previous query.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressTransactionsChain" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-transactions-mempool">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-address-transactions-mempool">GET Address Transactions Mempool <span>Addresses</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressTransactionsMempool)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/mempool</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressTransactionsMempool" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-utxo">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-address-utxo">GET Address UTXO <span>Addresses</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.addressUTXO)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/utxo</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.addressUTXO" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'">
|
||||
|
||||
<div class="endpoint-container" id="get-asset">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-assets">GET Asset <span>Assets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.assets)" target="_blank">GET {{ baseNetworkUrl }}/api/asset/:asset_id</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns information about a Liquid asset.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assets" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-asset-transactions">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-asset-transactions">GET Asset Transactions <span>Assets</span></a>
|
||||
<div class="endpoint">
|
||||
<a [href]="wrapUrl(network.val, code.assetTransactions)" target="_blank">GET {{ baseNetworkUrl }}/api/asset/:asset_id/txs[/mempool|/chain]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns transactions associated with the specified Liquid asset. For the network's native asset, returns a list of peg in, peg out, and burn transactions. For user-issued assets, returns a list of issuance, reissuance, and burn transactions. Does not include regular transactions transferring this asset.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assetTransactions" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-asset-supply">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-asset-supply">GET Asset Supply <span>Assets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.assetSupply)" target="_blank">GET {{ baseNetworkUrl }}/api/asset/:asset_id/supply[/decimal]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Get the current total supply of the specified asset. For the native asset (L-BTC), this is calculated as [chain,mempool]_stats.peg_in_amount - [chain,mempool]_stats.peg_out_amount - [chain,mempool]_stats.burned_amount. For issued assets, this is calculated as [chain,mempool]_stats.issued_amount - [chain,mempool]_stats.burned_amount. Not available for assets with blinded issuances. If /decimal is specified, returns the supply as a decimal according to the asset's divisibility. Otherwise, returned in base units.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assetSupply" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-asset-icons">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-assets-icons">GET Asset Icons <span>Assets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.assetIcons)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/assets/icons</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div>Get all the Asset IDs that have icons.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assetIcons" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-asset-icon">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-asset-icon">GET Asset Icon <span>Assets</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.assetIcon)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/asset/:asset_id/icon</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div>Get the icon of the specified asset.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.assetIcon" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category">
|
||||
|
||||
<div class="endpoint-container" id="get-block">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block">GET Block <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.block)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.block" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-header">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-header">GET Block Header <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockHeader)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/header</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the hex-encoded block header.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockHeader" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-height">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-height">GET Block Height <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockHeight)" target="_blank">GET {{ baseNetworkUrl }}/api/block-height/:height</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the hash of the block currently at <code>:height</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockHeight" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-raw">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-raw">GET Block Raw <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/raw</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the raw block representation in binary.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockRaw" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-status">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-status">GET Block Status <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/status</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the confirmation status of a block. Available fields: <code>in_best_chain</code> (boolean, false for orphaned blocks), <code>next_best</code> (the hash of the next block, only available for blocks in the best chain).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockStatus" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-block-tip-height">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-tip-height">GET Block Tip Height <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTipHeight)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/height</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the height of the last block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTipHeight" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-tip-hash">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-tip-hash">GET Block Tip Hash <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTipHash)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/hash</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the hash of the last block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTipHash" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-transaction-id">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-transaction-id">GET Block Transaction ID <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTxId)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txid/:index</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the transaction at index <code>:index</code> within the specified block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTxId" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-transaction-ids">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-transaction-ids">GET Block Transaction IDs <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTxIds)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txids</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a list of all txids in the block.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTxIds" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-transactions">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-block-transactions">GET Block Transactions <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blockTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txs[/:start_index]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a list of transactions in the block (up to 25 transactions beginning at <code>start_index</code>). Transactions returned here do not have the <code>status</code> field, since all the transactions share the same block and confirmation status.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blockTxs" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-blocks">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-blocks">GET Blocks <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blocks)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks[/:start_height]</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blocks" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val === 'bisq'" id="get-blocks">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-blocks">GET Blocks <span>Blocks</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.blocksBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/:index/:length</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.blocksBisq" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq'">
|
||||
|
||||
<div class="endpoint-container" id="get-mempool-blocks-fees">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-mempool-blocks-fees">GET Mempool Blocks Fees <span>Fees</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.feeMempoolBlocks)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/mempool-blocks</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.fees.mempool-blocks|API Docs for /api/v1/fees/mempool-blocks">Returns current mempool as projected blocks.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.feeMempoolBlocks" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-recommended-fees">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-recommended-fees">GET Recommended Fees <span>Fees</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.feeRecommended)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/recommended</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.fees.recommended|API Docs for /api/v1/fees/recommended">Returns our currently suggested fees for new transactions.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.feeRecommended" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq'">
|
||||
|
||||
<div class="endpoint-container" id="get-mempool">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-mempool">GET Mempool <span>Fees</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.mempool)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.mempool.mempool|API Docs for /api/mempool">Returns current mempool backlog statistics.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.mempool" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-mempool-transaction-ids">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-mempool-transaction-ids">GET Mempool Transactions IDs <span>Fees</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.mempoolTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/txids</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.mempool.txids|API Docs for /api/mempool/txids">Get the full list of txids in the mempool as an array. The order of the txids is arbitrary and does not match bitcoind.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.mempoolTxs" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-mempool-recent">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-mempool-recent">GET Mempool Recent <span>Fees</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.mempoolRecent)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/recent</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.mempool.recent|API Docs for /api/mempool/recent">Get a list of the last 10 transactions to enter the mempool. Each transaction object contains simplified overview data, with the following fields: <code>txid</code>, <code>fee</code>, <code>vsize</code>, and <code>value</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.mempoolRecent" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="api-category">
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-cpfp">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-cpfp">GET Children Pay for Parent <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionCpfp)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/cpfp/:txid</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.fees.cpfp|API Docs for /api/v1/fees/cpfp">Returns the ancestors and the best descendant fees for a transaction.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionCpfp" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" id="get-transaction">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction">GET Transaction <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transaction)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns details about a transaction. Available fields: <code>txid</code>, <code>version</code>, <code>locktime</code>, <code>size</code>, <code>weight</code>, <code>fee</code>, <code>vin</code>, <code>vout</code>, and <code>status</code>.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transaction" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-hex">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-hex">GET Transaction Hex <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionHex)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/hex</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a transaction serialized as hex.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionHex" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq' && network.val !== 'liquid' && network.val !== 'liquidtestnet'" id="get-transaction-merkleblock-proof">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-merkleblock-proof">GET Transaction Merkleblock Proof <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionMerkleBlockProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkleblock-proof</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionMerkleBlockProof" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-merkle-proof">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-merkle-proof">GET Transaction Merkle Proof <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionMerkleProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkle-proof</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a merkle inclusion proof for the transaction using <a href="https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-transaction-get-merkle">Electrum's blockchain.transaction.get_merkle format.</a></div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionMerkleProof" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-outspend">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-outspend">GET Transaction Outspend <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionOutspend)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspend/:vout</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the spending status of a transaction output. Available fields: <code>spent</code> (boolean), <code>txid</code> (optional), <code>vin</code> (optional), and <code>status</code> (optional, the status of the spending tx).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionOutspend" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-outspends">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-outspends">GET Transaction Outspends <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionOutspends)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspends</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the spending status of all transaction outputs.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionOutspends" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-raw">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-raw">GET Transaction Raw <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/raw</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns a transaction as binary data.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionRaw" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-status">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transaction-status">GET Transaction Status <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/status</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns the confirmation status of a transaction. Available fields: <code>confirmed</code> (boolean), <code>block_height</code> (optional), and <code>block_hash</code> (optional).</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionStatus" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val === 'bisq'" id="get-transactions">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="get-transactions">GET Transactions <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<a [href]="wrapUrl(network.val, code.transactionsBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/txs/:index/:length</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Returns :length of latest Bisq transactions, starting from :index.</div>
|
||||
</div>
|
||||
<app-code-template [hostname]="hostname" [code]="code.transactionsBisq" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
<div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="post-transaction">
|
||||
<a class="section-header" [routerLink]="['./']" fragment="post-transaction">POST Transaction <span>Transactions</span></a>
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
<div>POST {{ baseNetworkUrl }}/api/tx</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n>Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The <code>txid</code> will be returned on success.</div>
|
||||
</div>
|
||||
<app-code-template [method]="'post'" [hostname]="hostname" [code]="code.transactionPost" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="websocketAPI" *ngIf="!restTabActivated && ( network.val !== 'bisq' )">
|
||||
<div class="api-category">
|
||||
<div class="websocket">
|
||||
<div class="endpoint">
|
||||
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
|
||||
{{ wrapUrl(network.val, code.websocket, true) }}
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="subtitle" i18n>Description</div>
|
||||
<div i18n="api-docs.websocket.websocket">Default push: <code>{{ '{' }} action: 'want', data: ['blocks', ...] {{ '}' }}</code> to express what you want pushed. Available: <code>blocks</code>, <code>mempool-blocks</code>, <code>live-2h-chart</code>, and <code>stats</code>.<br><br>Push transactions related to address: <code>{{ '{' }} 'track-address': '3PbJ...bF9B' {{ '}' }}</code> to receive all new transactions containing that address as input or output. Returns an array of transactions. <code>address-transactions</code> for new mempool transactions, and <code>block-transactions</code> for new block confirmed transactions.</div>
|
||||
</div>
|
||||
<app-code-template [method]="'websocket'" [hostname]="hostname" [code]="code.websocket" [network]="network.val" ></app-code-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
206
frontend/src/app/components/docs/api-docs.component.scss
Normal file
206
frontend/src/app/components/docs/api-docs.component.scss
Normal file
@@ -0,0 +1,206 @@
|
||||
.text-small {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #1d1f31;
|
||||
font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New;
|
||||
}
|
||||
|
||||
tr {
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
li.nav-item {
|
||||
width: 100%;
|
||||
@media (min-width: 676px){
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.no-bottom-space {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
border-bottom: 1px solid #fff;
|
||||
@media (min-width: 676px){
|
||||
border-bottom: 1px solid #11131f;
|
||||
}
|
||||
}
|
||||
|
||||
.code-tab {
|
||||
width: auto;
|
||||
margin: 20px auto 10px;
|
||||
li.nav-item {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.code {
|
||||
.tab-content {
|
||||
padding: 0px;
|
||||
}
|
||||
.nav-tabs .nav-link.active {
|
||||
border-bottom: 1px solid #11131f;
|
||||
}
|
||||
.subtitle {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.description {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.title {
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.subtitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
.divider {
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
height: 1px;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.difficulty {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
#doc-nav-desktop {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#doc-nav-desktop.relative {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#doc-nav-desktop.fixed {
|
||||
float: unset;
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
overflow-y: auto;
|
||||
height: calc(100vh - 50px);
|
||||
scrollbar-color: #2d3348 #11131f;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #11131f;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #2d3348;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.doc-content {
|
||||
width: calc(100% - 330px);
|
||||
float: right;
|
||||
}
|
||||
|
||||
.endpoint-container:before {
|
||||
display: block;
|
||||
content: " ";
|
||||
height: 1px;
|
||||
margin-top: -1px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header {
|
||||
display: block;
|
||||
background-color: #2d3348;
|
||||
color: #1bd8f4;
|
||||
padding: 1rem 1.3rem 1rem 1.3rem;
|
||||
font-weight: bold;
|
||||
border-radius: 0.25rem;
|
||||
margin: 20px 0 20px 0;
|
||||
font-size: 24px;
|
||||
position: relative;
|
||||
}
|
||||
.endpoint-container .section-header:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header span {
|
||||
color: #fff;
|
||||
background-color: #653b9c;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
padding: 8px 10px;
|
||||
letter-spacing: 1px;
|
||||
border-radius: 0.25rem;
|
||||
font-family: monospace;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#doc-nav-mobile {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
width: calc(100% - 60px);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#doc-nav-mobile > div {
|
||||
background-color: #2d3348;
|
||||
z-index: 100;
|
||||
border-radius: 0 0 0.5rem 0.5rem;
|
||||
height: 55vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#doc-nav-mobile button {
|
||||
width: 100%;
|
||||
background-color: #105fb0;
|
||||
color: #fff;
|
||||
border-color: #105fb0;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
|
||||
.hide-on-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.doc-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header {
|
||||
margin: 40px 0 70px 0;
|
||||
}
|
||||
|
||||
.endpoint-container .section-header span {
|
||||
float: none;
|
||||
position: absolute;
|
||||
top: unset;
|
||||
left: 0;
|
||||
bottom: -50px;
|
||||
}
|
||||
|
||||
.endpoint-container:before {
|
||||
height: 30px;
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.hide-on-desktop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
<pre><code [innerText]="wrapCurlTemplate(code)"></code></pre>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li ngbNavItem>
|
||||
<li ngbNavItem *ngIf="network !== 'liquidtestnet'">
|
||||
<a ngbNavLink>CommonJS</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="subtitle"><ng-container i18n="API Docs code example">Code Example</ng-container> <app-clipboard [text]="wrapCommonJS(code)"></app-clipboard></div>
|
||||
@@ -18,7 +18,7 @@
|
||||
</ng-template>
|
||||
</li>
|
||||
<li ngbNavItem>
|
||||
<a ngbNavLink>ES Module</a>
|
||||
<a ngbNavLink *ngIf="network !== 'liquidtestnet'">ES Module</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="subtitle"><ng-container i18n="API Docs install lib">Install Package</ng-container> <app-clipboard [text]="wrapImportTemplate()"></app-clipboard></div>
|
||||
<div class="links">
|
||||
@@ -26,7 +26,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
if (this.network === 'bisq') {
|
||||
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-bisq-js`;
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
npmLink = `https://github.com/mempool/mempool.js/tree/main/npm-liquid-js`;
|
||||
}
|
||||
return npmLink;
|
||||
@@ -37,7 +37,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
if (this.network === 'bisq') {
|
||||
npmLink = `https://www.npmjs.org/package/@mempool/bisq.js`;
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
npmLink = `https://www.npmjs.org/package/@mempool/liquid.js`;
|
||||
}
|
||||
return npmLink;
|
||||
@@ -50,7 +50,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
} else {
|
||||
codeText = codeText.replace('%{0}', 'bitcoin');
|
||||
}
|
||||
if(['', 'main', 'liquid', 'bisq'].includes(this.network)) {
|
||||
if(['', 'main', 'liquid', 'bisq', 'liquidtestnet'].includes(this.network)) {
|
||||
codeText = codeText.replace('mempoolJS();', `mempoolJS({
|
||||
hostname: '${document.location.hostname}'
|
||||
});`);
|
||||
@@ -119,7 +119,7 @@ export class CodeTemplateComponent implements OnInit {
|
||||
if (this.network === 'signet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
@@ -157,13 +157,17 @@ init();`;
|
||||
if (this.network === 'signet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleSignet.esModule);
|
||||
}
|
||||
if (this.network === 'liquid') {
|
||||
if (this.network === 'liquid' || this.network === 'liquidtestnet') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleLiquid.esModule);
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule);
|
||||
}
|
||||
|
||||
if (code.noWrap) {
|
||||
return codeText;
|
||||
}
|
||||
|
||||
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>`;
|
||||
@@ -236,6 +240,9 @@ yarn add @mempool/liquid.js`;
|
||||
if (this.network === 'liquid') {
|
||||
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquid);
|
||||
}
|
||||
if (this.network === 'liquidtestnet') {
|
||||
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleLiquidTestnet);
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
return this.replaceCurlPlaceholder(code.codeTemplate.curl, code.codeSampleBisq);
|
||||
}
|
||||
@@ -258,6 +265,9 @@ yarn add @mempool/liquid.js`;
|
||||
if (this.network === 'liquid') {
|
||||
return code.codeSampleLiquid.response;
|
||||
}
|
||||
if (this.network === 'liquidtestnet') {
|
||||
return code.codeSampleLiquidTestnet.response;
|
||||
}
|
||||
if (this.network === 'bisq') {
|
||||
return code.codeSampleBisq.response;
|
||||
}
|
||||
@@ -293,10 +303,18 @@ yarn add @mempool/liquid.js`;
|
||||
return `curl -X POST -sSLd "${text}"`;
|
||||
}
|
||||
return `curl -sSL "${this.hostname}/${this.network}${text}"`;
|
||||
} else if (this.env.BASE_MODULE === 'liquid') {
|
||||
if (this.method === 'post') {
|
||||
if (this.network !== 'liquid') {
|
||||
text = text.replace('/api', `/${this.network}/api`);
|
||||
}
|
||||
return `curl -X POST -sSLd "${text}"`;
|
||||
}
|
||||
return ( this.network === 'liquid' ? `curl -sSL "${this.hostname}${text}"` : `curl -sSL "${this.hostname}/${this.network}${text}"` );
|
||||
} else {
|
||||
return `curl -sSL "${this.hostname}${text}"`;
|
||||
}
|
||||
if (this.env.BASE_MODULE !== 'mempool') {
|
||||
return `curl -sSL "${this.hostname}${text}"`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
39
frontend/src/app/components/docs/docs.component.html
Normal file
39
frontend/src/app/components/docs/docs.component.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<div class="container-xl">
|
||||
<div class="text-center">
|
||||
|
||||
<h2 i18n="documentation.title">Documentation</h2>
|
||||
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-tabs">
|
||||
|
||||
<li [ngbNavItem]="0">
|
||||
<a ngbNavLink routerLink="../rest">API - REST</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<app-api-docs [restTabActivated]="true"></app-api-docs>
|
||||
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="1" *ngIf="showWebSocketTab">
|
||||
<a ngbNavLink routerLink="../websocket">API - WebSocket</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<app-api-docs [restTabActivated]="false"></app-api-docs>
|
||||
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div id="main-tab-content" [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
|
||||
<br>
|
||||
|
||||
<div id="footer" class="text-center">
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
|
|
||||
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
9
frontend/src/app/components/docs/docs.component.scss
Normal file
9
frontend/src/app/components/docs/docs.component.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
#main-tab-content {
|
||||
text-align: left;
|
||||
padding-top: 10px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
#footer {
|
||||
clear: both;
|
||||
}
|
||||
27
frontend/src/app/components/docs/docs.component.ts
Normal file
27
frontend/src/app/components/docs/docs.component.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Env, StateService } from 'src/app/services/state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-docs',
|
||||
templateUrl: './docs.component.html',
|
||||
styleUrls: ['./docs.component.scss']
|
||||
})
|
||||
export class DocsComponent implements OnInit {
|
||||
|
||||
activeTab = 0;
|
||||
env: Env;
|
||||
showWebSocketTab = true;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
const url = this.route.snapshot.url;
|
||||
this.activeTab = ( url[2].path === "rest" ) ? 0 : 1;
|
||||
this.env = this.stateService.env;
|
||||
this.showWebSocketTab = ( ! ( ( this.env.BASE_MODULE === "bisq" ) || ( this.stateService.network === "bisq" ) || ( this.stateService.network === "liquidtestnet" ) ) );
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export class FeesBoxComponent implements OnInit {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.defaultFee = this.stateService.network === 'liquid' ? 0.1 : 1;
|
||||
this.defaultFee = this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ? 0.1 : 1;
|
||||
|
||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||
this.feeEstimations$ = this.stateService.mempoolBlocks$
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { formatDate } from '@angular/common';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { OnChanges } from '@angular/core';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-incoming-transactions-graph',
|
||||
@@ -25,6 +25,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
@Input() top: number | string = '20';
|
||||
@Input() left: number | string = '0';
|
||||
@Input() template: ('widget' | 'advanced') = 'widget';
|
||||
@Input() windowPreferenceOverride: string;
|
||||
|
||||
isLoading = true;
|
||||
mempoolStatsChartOption: EChartsOption = {};
|
||||
@@ -46,7 +47,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.windowPreference = this.storageService.getValue('graphWindowPreference');
|
||||
this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference');
|
||||
this.mountChart();
|
||||
}
|
||||
|
||||
@@ -66,17 +67,21 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
left: this.left,
|
||||
},
|
||||
animation: false,
|
||||
dataZoom: [{
|
||||
dataZoom: (this.template === 'widget' && this.isMobile()) ? null : [{
|
||||
type: 'inside',
|
||||
realtime: true,
|
||||
zoomLock: (this.template === 'widget') ? true : false,
|
||||
zoomOnMouseWheel: (this.template === 'advanced') ? true : false,
|
||||
moveOnMouseMove: (this.template === 'widget') ? true : false,
|
||||
maxSpan: 100,
|
||||
minSpan: 10,
|
||||
}, {
|
||||
showDetail: false,
|
||||
show: (this.template === 'advanced') ? true : false,
|
||||
type: 'slider',
|
||||
brushSelect: false,
|
||||
realtime: true,
|
||||
bottom: 0,
|
||||
selectedDataBackground: {
|
||||
lineStyle: {
|
||||
color: '#fff',
|
||||
@@ -85,9 +90,10 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
areaStyle: {
|
||||
opacity: 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
}],
|
||||
tooltip: {
|
||||
show: !this.isMobile(),
|
||||
trigger: 'axis',
|
||||
position: (pos, params, el, elRect, size) => {
|
||||
const obj = { top: -20 };
|
||||
@@ -102,29 +108,39 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
type: 'line',
|
||||
},
|
||||
formatter: (params: any) => {
|
||||
const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue);
|
||||
const colorSpan = (color: string) => `<span class="indicator" style="background-color: ` + color + `"></span>`;
|
||||
let itemFormatted = '<div class="title">' + params[0].axisValue + '</div>';
|
||||
let itemFormatted = '<div class="title">' + axisValueLabel + '</div>';
|
||||
params.map((item: any, index: number) => {
|
||||
if (index < 26) {
|
||||
itemFormatted += `<div class="item">
|
||||
<div class="indicator-container">${colorSpan(item.color)}</div>
|
||||
<div class="grow"></div>
|
||||
<div class="value">${item.value} <span class="symbol">vB/s</span></div>
|
||||
<div class="value">${item.value[1]} <span class="symbol">vB/s</span></div>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
return `<div class="tx-wrapper-tooltip-chart ${(this.template === 'advanced') ? 'tx-wrapper-tooltip-chart-advanced' : ''}">${itemFormatted}</div>`;
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
axisLabel: {
|
||||
align: 'center',
|
||||
fontSize: 11,
|
||||
lineHeight: 12
|
||||
},
|
||||
data: this.data.labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`),
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
name: formatterXAxisLabel(this.locale, this.windowPreference),
|
||||
nameLocation: 'middle',
|
||||
nameTextStyle: {
|
||||
padding: [20, 0, 0, 0],
|
||||
},
|
||||
type: 'time',
|
||||
axisLabel: {
|
||||
margin: 20,
|
||||
align: 'center',
|
||||
fontSize: 11,
|
||||
lineHeight: 12,
|
||||
hideOverlap: true,
|
||||
padding: [0, 5],
|
||||
},
|
||||
}
|
||||
],
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
@@ -204,4 +220,8 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return window.innerWidth <= 767.98;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Language, languages } from 'src/app/app.constants';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { languages } from 'src/app/app.constants';
|
||||
import { LanguageService } from 'src/app/services/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-language-selector',
|
||||
@@ -12,42 +12,25 @@ import { StateService } from 'src/app/services/state.service';
|
||||
})
|
||||
export class LanguageSelectorComponent implements OnInit {
|
||||
languageForm: FormGroup;
|
||||
languages: Language[];
|
||||
languages = languages;
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
private formBuilder: FormBuilder,
|
||||
private stateService: StateService,
|
||||
@Inject(DOCUMENT) private document: Document
|
||||
private languageService: LanguageService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.languages = languages;
|
||||
|
||||
this.languageForm = this.formBuilder.group({
|
||||
language: ['']
|
||||
language: ['en']
|
||||
});
|
||||
this.setLanguageFromUrl();
|
||||
}
|
||||
|
||||
setLanguageFromUrl() {
|
||||
const urlLanguage = this.document.location.pathname.split('/')[1];
|
||||
if (this.languages.map((lang) => lang.code).indexOf(urlLanguage) > -1) {
|
||||
this.languageForm.get('language').setValue(urlLanguage);
|
||||
} else {
|
||||
this.languageForm.get('language').setValue('en');
|
||||
}
|
||||
this.languageForm.get('language').setValue(this.languageService.getLanguage());
|
||||
}
|
||||
|
||||
changeLanguage() {
|
||||
const language = this.languageForm.get('language').value;
|
||||
try {
|
||||
document.cookie = `lang=${language}; expires=Thu, 18 Dec 2050 12:00:00 UTC; path=/`;
|
||||
} catch (e) { }
|
||||
|
||||
if (this.stateService.env.BASE_MODULE === 'mempool') {
|
||||
this.document.location.href = `/${language}/${this.stateService.network}`;
|
||||
} else {
|
||||
this.document.location.href = `/${language}`;
|
||||
}
|
||||
const newLang = this.languageForm.get('language').value;
|
||||
this.languageService.setLanguage(newLang);
|
||||
const rawUrlPath = this.languageService.stripLanguageFromUrl(null);
|
||||
this.document.location.href = (newLang !== 'en' ? `/${newLang}` : '') + rawUrlPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<ng-container *ngIf="{ val: network$ | async } as network">
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
|
||||
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
||||
@@ -10,22 +11,23 @@
|
||||
</ng-container>
|
||||
</a>
|
||||
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||
<div ngbDropdown (window:resize)="onResize($event)" class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED || env.LIQUID_TESTNET_ENABLED">
|
||||
<button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split" aria-haspopup="true">
|
||||
<img src="./resources/liquid-logo.png" style="width: 25px; height: 25px;" class="mr-1">
|
||||
<img src="./resources/{{ network.val === '' ? 'liquid' : network.val }}-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>
|
||||
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage" ngbDropdownItem class="mainnet"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1"> Mainnet</a>
|
||||
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + '/signet'" ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet"><img src="./resources/signet-logo.png" style="width: 30px;" class="mr-1"> Signet</a>
|
||||
<a [href]="env.MEMPOOL_WEBSITE_URL + urlLanguage + '/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>
|
||||
<a href="https://bisq.markets" ngbDropdownItem class="mainnet"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a>
|
||||
<button ngbDropdownItem class="liquid active" routerLink="/"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
<a [href]="env.BISQ_WEBSITE_URL + urlLanguage" ngbDropdownItem class="mainnet"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a>
|
||||
<button ngbDropdownItem class="liquid" [class.active]="network.val === 'liquid'" routerLink="/"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
<button ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet" [class.active]="network.val === 'liquidtestnet'" routerLink="/testnet"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav liquid">
|
||||
<ul class="navbar-nav {{ network.val }}">
|
||||
<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>
|
||||
</li>
|
||||
@@ -41,10 +43,10 @@
|
||||
</li>
|
||||
-->
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/assets']" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
<a class="nav-link" [routerLink]="['/assets' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
</li>
|
||||
<li [hidden]="isMobile" 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]="['/docs' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'book']" [fixedWidth]="true" i18n-title="master-page.docs" title="Docs"></fa-icon></a>
|
||||
</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>
|
||||
@@ -59,4 +61,5 @@
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
</ng-container>
|
||||
@@ -102,6 +102,10 @@ nav {
|
||||
background-color: #116761;
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Env, StateService } from '../../services/state.service';
|
||||
import { Observable} from 'rxjs';
|
||||
import { merge, Observable, of} from 'rxjs';
|
||||
import { LanguageService } from 'src/app/services/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-liquid-master-page',
|
||||
@@ -13,14 +14,19 @@ export class LiquidMasterPageComponent implements OnInit {
|
||||
navCollapsed = false;
|
||||
isMobile = window.innerWidth <= 767.98;
|
||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||
network$: Observable<string>;
|
||||
urlLanguage: string;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private languageService: LanguageService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.env = this.stateService.env;
|
||||
this.connectionState$ = this.stateService.connectionState$;
|
||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||
this.urlLanguage = this.languageService.getLanguageForUrl();
|
||||
}
|
||||
|
||||
collapse(): void {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</ng-container>
|
||||
</a>
|
||||
|
||||
<div (window:resize)="onResize($event)" ngbDropdown class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED">
|
||||
<div (window:resize)="onResize($event)" ngbDropdown class="dropdown-container" *ngIf="env.TESTNET_ENABLED || env.SIGNET_ENABLED || env.LIQUID_ENABLED || env.BISQ_ENABLED || env.LIQUID_TESTNET_ENABLED">
|
||||
<button ngbDropdownToggle type="button" class="btn btn-secondary dropdown-toggle-split" aria-haspopup="true">
|
||||
<img src="./resources/{{ network.val === '' ? 'bitcoin' : network.val }}-logo.png" style="width: 25px; height: 25px;" class="mr-1">
|
||||
</button>
|
||||
@@ -20,10 +20,9 @@
|
||||
<button ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet" [class.active]="network.val === 'signet'" routerLink="/signet"><img src="./resources/signet-logo.png" style="width: 30px;" class="mr-1"> Signet</button>
|
||||
<button ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1"> Testnet</button>
|
||||
<h6 *ngIf="env.LIQUID_ENABLED || env.BISQ_ENABLED" class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
|
||||
<a href="https://bisq.markets" ngbDropdownItem *ngIf="env.BISQ_ENABLED && env.OFFICIAL_MEMPOOL_SPACE" class="bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a>
|
||||
<button ngbDropdownItem *ngIf="env.BISQ_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="bisq" [class.active]="network.val === 'bisq'" routerLink="/bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
|
||||
<a href="https://liquid.network" ngbDropdownItem *ngIf="env.LIQUID_ENABLED && env.OFFICIAL_MEMPOOL_SPACE" class="liquid" [class.active]="network.val === 'liquid'"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a>
|
||||
<button ngbDropdownItem *ngIf="env.LIQUID_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="liquid" [class.active]="network.val === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
<a [href]="env.BISQ_WEBSITE_URL + urlLanguage" ngbDropdownItem *ngIf="env.BISQ_ENABLED" class="bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a>
|
||||
<a [href]="env.LIQUID_WEBSITE_URL + urlLanguage" ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid" [class.active]="network.val === 'liquid'"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a>
|
||||
<a [href]="env.LIQUID_WEBSITE_URL + urlLanguage + '/testnet'" ngbDropdownItem *ngIf="env.LIQUID_TESTNET_ENABLED" class="liquidtestnet" [class.active]="network.val === 'liquid'"><img src="./resources/liquidtestnet-logo.png" style="width: 30px;" class="mr-1"> Liquid Testnet</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -54,11 +53,11 @@
|
||||
<a class="nav-link" [routerLink]="['/tv' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tv']" [fixedWidth]="true" i18n-title="master-page.tvview" title="TV view"></fa-icon></a>
|
||||
</li>
|
||||
</ng-template>
|
||||
<li *ngIf="network.val === 'liquid'" class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/liquid/assets']" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
<li *ngIf="network.val === 'liquid' || network.val === 'liquidtestnet'" class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/assets' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" [fixedWidth]="true" i18n-title="master-page.assets" title="Assets"></fa-icon></a>
|
||||
</li>
|
||||
<li [hidden]="isMobile" 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>
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/docs' | relativeUrl ]" (click)="collapse()"><fa-icon [icon]="['fas', 'book']" [fixedWidth]="true" i18n-title="documentation.title" title="Documentation"></fa-icon></a>
|
||||
</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>
|
||||
|
||||
@@ -110,6 +110,10 @@ nav {
|
||||
background-color: #116761;
|
||||
}
|
||||
|
||||
.liquidtestnet.active {
|
||||
background-color: #494a4a;
|
||||
}
|
||||
|
||||
.testnet.active {
|
||||
background-color: #1d486f;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Env, StateService } from '../../services/state.service';
|
||||
import { Observable, merge, of } from 'rxjs';
|
||||
import { LanguageService } from 'src/app/services/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-master-page',
|
||||
@@ -14,15 +15,18 @@ export class MasterPageComponent implements OnInit {
|
||||
navCollapsed = false;
|
||||
isMobile = window.innerWidth <= 767.98;
|
||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||
urlLanguage: string;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private languageService: LanguageService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.env = this.stateService.env;
|
||||
this.connectionState$ = this.stateService.connectionState$;
|
||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||
this.urlLanguage = this.languageService.getLanguageForUrl();
|
||||
}
|
||||
|
||||
collapse(): void {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<div class="flashing">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
||||
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
|
||||
<a draggable="false" [routerLink]="['/mempool-block/' | relativeUrl, i]"
|
||||
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}"> </a>
|
||||
<div class="block-body">
|
||||
<div class="fees">
|
||||
~{{ projectedBlock.medianFee | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
@@ -18,7 +19,7 @@
|
||||
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
|
||||
</div>
|
||||
<div class="time-difference" *ngIf="projectedBlock.blockVSize <= stateService.blockVSize; else mergedBlock">
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
|
||||
<ng-template [ngIf]="network === 'liquid' || network === 'liquidtestnet'" [ngIfElse]="timeDiffMainnet">
|
||||
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
||||
</ng-template>
|
||||
<ng-template #timeDiffMainnet>
|
||||
|
||||
@@ -117,6 +117,10 @@
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.blockLink.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blockLink:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Router } from '@angular/router';
|
||||
import { take, map, switchMap } from 'rxjs/operators';
|
||||
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||
import { specialBlocks } from 'src/app/app.constants';
|
||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-blocks',
|
||||
@@ -52,10 +53,11 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
private router: Router,
|
||||
public stateService: StateService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
|
||||
this.feeRounding = '1.0-1';
|
||||
}
|
||||
this.mempoolEmptyBlocks.forEach((b) => {
|
||||
@@ -166,19 +168,19 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
if (event.key === 'ArrowRight') {
|
||||
if (this.mempoolBlocks[this.markIndex - 1]) {
|
||||
this.router.navigate([(this.network ? '/' + this.network : '') + '/mempool-block/', this.markIndex - 1]);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('mempool-block/'), this.markIndex - 1]);
|
||||
} else {
|
||||
this.stateService.blocks$
|
||||
.pipe(take(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT))
|
||||
.subscribe(([block]) => {
|
||||
if (this.stateService.latestBlockHeight === block.height) {
|
||||
this.router.navigate([(this.network ? '/' + this.network : '') + '/block/', block.id], { state: { data: { block } }});
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/block/'), block.id], { state: { data: { block } }});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (event.key === 'ArrowLeft') {
|
||||
if (this.mempoolBlocks[this.markIndex + 1]) {
|
||||
this.router.navigate([(this.network ? '/' + this.network : '') + '/mempool-block/', this.markIndex + 1]);
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/mempool-block/'), this.markIndex + 1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core';
|
||||
import { formatDate } from '@angular/common';
|
||||
import { VbytesPipe } from 'src/app/shared/pipes/bytes-pipe/vbytes.pipe';
|
||||
import { formatNumber } from "@angular/common";
|
||||
|
||||
import { OptimizedMempoolStats } from 'src/app/interfaces/node-api.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { EChartsOption } from 'echarts';
|
||||
import { feeLevels, chartColors } from 'src/app/app.constants';
|
||||
import { formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-graph',
|
||||
@@ -32,6 +31,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
@Input() left: number | string = 75;
|
||||
@Input() template: ('widget' | 'advanced') = 'widget';
|
||||
@Input() showZoom = true;
|
||||
@Input() windowPreferenceOverride: string;
|
||||
|
||||
isLoading = true;
|
||||
mempoolVsizeFeesData: any;
|
||||
@@ -62,7 +62,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.windowPreference = this.storageService.getValue('graphWindowPreference');
|
||||
this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference');
|
||||
this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([]));
|
||||
this.mountFeeChart();
|
||||
}
|
||||
@@ -97,13 +97,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
generateArray(mempoolStats: OptimizedMempoolStats[]) {
|
||||
const finalArray: number[][] = [];
|
||||
let feesArray: number[] = [];
|
||||
const finalArray: number[][][] = [];
|
||||
let feesArray: number[][] = [];
|
||||
let limitFeesTemplate = this.template === 'advanced' ? 26 : 20;
|
||||
for (let index = limitFeesTemplate; index > -1; index--) {
|
||||
feesArray = [];
|
||||
mempoolStats.forEach((stats) => {
|
||||
feesArray.push(stats.vsizes[index] ? stats.vsizes[index] : 0);
|
||||
feesArray.push([stats.added * 1000, stats.vsizes[index] ? stats.vsizes[index] : 0]);
|
||||
});
|
||||
finalArray.push(feesArray);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
|
||||
mountFeeChart() {
|
||||
this.orderLevels();
|
||||
const { labels, series } = this.mempoolVsizeFeesData;
|
||||
const { series } = this.mempoolVsizeFeesData;
|
||||
|
||||
const seriesGraph = [];
|
||||
const newColors = [];
|
||||
@@ -170,7 +170,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
hover: true,
|
||||
color: this.inverted ? [...newColors].reverse() : newColors,
|
||||
tooltip: {
|
||||
show: (window.innerWidth >= 768) ? true : false,
|
||||
show: !this.isMobile(),
|
||||
trigger: 'axis',
|
||||
alwaysShowContent: false,
|
||||
position: (pos, params, el, elRect, size) => {
|
||||
@@ -186,14 +186,15 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
type: 'line',
|
||||
},
|
||||
formatter: (params: any) => {
|
||||
const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue);
|
||||
const { totalValue, totalValueArray } = this.getTotalValues(params);
|
||||
const itemFormatted = [];
|
||||
let totalParcial = 0;
|
||||
let progressPercentageText = '';
|
||||
const items = this.inverted ? [...params].reverse() : params;
|
||||
items.map((item: any, index: number) => {
|
||||
totalParcial += item.value;
|
||||
const progressPercentage = (item.value / totalValue) * 100;
|
||||
totalParcial += item.value[1];
|
||||
const progressPercentage = (item.value[1] / totalValue) * 100;
|
||||
const progressPercentageSum = (totalValueArray[index] / totalValue) * 100;
|
||||
let activeItemClass = '';
|
||||
let hoverActive = 0;
|
||||
@@ -233,7 +234,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
</td>
|
||||
<td class="total-progress-sum">
|
||||
<span>
|
||||
${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)}
|
||||
${this.vbytesPipe.transform(item.value[1], 2, 'vB', 'MvB', false)}
|
||||
</span>
|
||||
</td>
|
||||
<td class="total-progress-sum">
|
||||
@@ -257,7 +258,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
const titleSum = $localize`Sum`;
|
||||
return `<div class="fees-wrapper-tooltip-chart ${classActive}">
|
||||
<div class="title">
|
||||
${params[0].axisValue}
|
||||
${axisValueLabel}
|
||||
<span class="total-value">
|
||||
${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)}
|
||||
</span>
|
||||
@@ -281,13 +282,16 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
</div>`;
|
||||
}
|
||||
},
|
||||
dataZoom: [{
|
||||
dataZoom: (this.template === 'widget' && this.isMobile()) ? null : [{
|
||||
type: 'inside',
|
||||
realtime: true,
|
||||
zoomLock: (this.template === 'widget') ? true : false,
|
||||
zoomOnMouseWheel: (this.template === 'advanced') ? true : false,
|
||||
moveOnMouseMove: (this.template === 'widget') ? true : false,
|
||||
maxSpan: 100,
|
||||
minSpan: 10,
|
||||
}, {
|
||||
showDetail: false,
|
||||
show: (this.template === 'advanced' && this.showZoom) ? true : false,
|
||||
type: 'slider',
|
||||
brushSelect: false,
|
||||
@@ -312,15 +316,22 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
name: formatterXAxisLabel(this.locale, this.windowPreference),
|
||||
nameLocation: 'middle',
|
||||
nameTextStyle: {
|
||||
padding: [20, 0, 0, 0],
|
||||
},
|
||||
type: 'time',
|
||||
boundaryGap: false,
|
||||
axisLine: { onZero: true },
|
||||
axisLabel: {
|
||||
margin: 20,
|
||||
align: 'center',
|
||||
fontSize: 11,
|
||||
lineHeight: 12,
|
||||
hideOverlap: true,
|
||||
padding: [0, 5],
|
||||
},
|
||||
data: labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`),
|
||||
}
|
||||
],
|
||||
yAxis: {
|
||||
@@ -346,7 +357,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
const totalValueArray = [];
|
||||
const valuesInverted = this.inverted ? values : [...values].reverse();
|
||||
for (const item of valuesInverted) {
|
||||
totalValueTemp += item.value;
|
||||
totalValueTemp += item.value[1];
|
||||
totalValueArray.push(totalValueTemp);
|
||||
}
|
||||
return {
|
||||
@@ -362,7 +373,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
this.feeLimitIndex = i;
|
||||
}
|
||||
if (feeLevels[i] <= this.limitFee) {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') {
|
||||
this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)} - ${(feeLevels[i + 1] / 10).toFixed(1)}`);
|
||||
} else {
|
||||
this.feeLevelsOrdered.push(`${feeLevels[i]} - ${feeLevels[i + 1]}`);
|
||||
@@ -371,5 +382,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
}
|
||||
this.chartColorsOrdered = chartColors.slice(0, this.feeLevelsOrdered.length);
|
||||
}
|
||||
|
||||
isMobile() {
|
||||
return window.innerWidth <= 767.98;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Input, AfterViewInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
||||
import * as QRCode from 'qrcode/build/qrcode.js';
|
||||
import { Component, Input, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
|
||||
import * as QRCode from 'qrcode';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
|
||||
@Component({
|
||||
@@ -23,7 +23,7 @@ export class QrcodeComponent implements AfterViewInit {
|
||||
if (!this.stateService.isBrowser) {
|
||||
return;
|
||||
}
|
||||
const opts = {
|
||||
const opts: QRCode.QRCodeRenderersOptions = {
|
||||
errorCorrectionLevel: 'H',
|
||||
margin: 0,
|
||||
color: {
|
||||
@@ -31,7 +31,6 @@ export class QrcodeComponent implements AfterViewInit {
|
||||
light: '#fff'
|
||||
},
|
||||
width: this.size,
|
||||
height: this.size,
|
||||
};
|
||||
|
||||
if (!this.data) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user