Compare commits
198 Commits
v2.3.0-dev
...
v2.3.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a7533013b | ||
|
|
eeb0f403a3 | ||
|
|
bcbc60b456 | ||
|
|
0d14c30892 | ||
|
|
49c30c7237 | ||
|
|
7ba0055c61 | ||
|
|
72b631a4dc | ||
|
|
b9a2143a5a | ||
|
|
815fb62e7d | ||
|
|
7a19da560e | ||
|
|
43a1af4509 | ||
|
|
cf1471acca | ||
|
|
a2a69e522c | ||
|
|
4939941c88 | ||
|
|
a643f50016 | ||
|
|
836444a1c5 | ||
|
|
24d4b643e5 | ||
|
|
7f045ae5b9 | ||
|
|
96a2c8ab4e | ||
|
|
47103d85d3 | ||
|
|
0af5d733c4 | ||
|
|
bc7bbf5fe5 | ||
|
|
4bc141cd67 | ||
|
|
1c6b3a46c6 | ||
|
|
b41e32915f | ||
|
|
adb5bfe93f | ||
|
|
f4b7bbc91c | ||
|
|
687b4af272 | ||
|
|
e67f552fbd | ||
|
|
28d3f190ff | ||
|
|
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 | ||
|
|
53b0bc0e48 | ||
|
|
e00234dfb9 | ||
|
|
8412e28073 | ||
|
|
10a110e682 | ||
|
|
1e40ec4fd0 | ||
|
|
9998a64fa5 | ||
|
|
d1e3c55a28 | ||
|
|
047220bd32 | ||
|
|
1ce05a3ac9 | ||
|
|
0271584b69 | ||
|
|
01da46daca | ||
|
|
73f558db6e | ||
|
|
feb8e35ec3 | ||
|
|
79e44479e9 | ||
|
|
9a39d3207f | ||
|
|
7e55e836fd | ||
|
|
571bf3fa64 | ||
|
|
0b1cf052a8 | ||
|
|
b301840bb8 | ||
|
|
a0e5a79ec4 | ||
|
|
8da89230c9 | ||
|
|
2408e81537 | ||
|
|
9377d80dbb | ||
|
|
8c200bc0e4 | ||
|
|
35ca8c846b | ||
|
|
4b1acfc77e | ||
|
|
8569523b89 | ||
|
|
9bef7da210 | ||
|
|
6f6b2a4355 | ||
|
|
783530b3de | ||
|
|
36777a8c5b | ||
|
|
638d6d896a | ||
|
|
2afb9d359f | ||
|
|
60a8d0ce6e | ||
|
|
e3aeb784ad | ||
|
|
8124274880 | ||
|
|
836f0f065d | ||
|
|
062fdb3658 | ||
|
|
7cfbd2c70d | ||
|
|
5b9ae2eaf5 | ||
|
|
d534c42c47 | ||
|
|
15a0644bd1 | ||
|
|
6ad4e655ea | ||
|
|
598920d6a9 | ||
|
|
d816e67ce4 | ||
|
|
662aafff57 | ||
|
|
cfec9345c8 | ||
|
|
9cb203f98a | ||
|
|
3bab50a43b | ||
|
|
0639ce9b07 | ||
|
|
34c9ca4197 | ||
|
|
14d015a904 | ||
|
|
38412753fe | ||
|
|
475f9344a0 | ||
|
|
d9cf6a2604 | ||
|
|
9eb159617f | ||
|
|
2dc930fad2 | ||
|
|
c478478f86 | ||
|
|
7a07f67be5 | ||
|
|
a3de75ebf4 | ||
|
|
46a2854f67 | ||
|
|
033f066abf | ||
|
|
97244c7b35 | ||
|
|
3d92369ed6 | ||
|
|
2bee46951c | ||
|
|
61ffcd0065 | ||
|
|
88e79c220f | ||
|
|
a2c21e9036 | ||
|
|
8a0316a562 | ||
|
|
055c1f2aa5 | ||
|
|
88e48df8a9 | ||
|
|
b81d296635 | ||
|
|
9f68f57d8a | ||
|
|
26a540a57c | ||
|
|
ad8398e3d4 | ||
|
|
0fabf8dbc3 | ||
|
|
cd63c6c0c1 | ||
|
|
c95f75254b | ||
|
|
9e4ad51b79 | ||
|
|
6e35102b3a | ||
|
|
d1e72c0cc0 | ||
|
|
1925023eb2 | ||
|
|
377eb0cae5 | ||
|
|
e33e4b1d71 | ||
|
|
b8f3c1f124 | ||
|
|
5333ec0b99 | ||
|
|
f761c5d966 | ||
|
|
3b6d07cace | ||
|
|
cbeeef3b2c | ||
|
|
5dab44e6c4 | ||
|
|
5139ffb4df | ||
|
|
06706be18f | ||
|
|
9264f3cf4f | ||
|
|
d6a9017267 | ||
|
|
bad75cfd4e | ||
|
|
68240e4f5c | ||
|
|
9d9ff6ed91 | ||
|
|
f19b84090a | ||
|
|
85c4574cbd | ||
|
|
e2c4e42c81 | ||
|
|
dd6c26b079 | ||
|
|
837992f7ea | ||
|
|
40914236d4 | ||
|
|
f52c36093e | ||
|
|
a9f4418e1a | ||
|
|
1a37c8b116 | ||
|
|
39d231bb3c | ||
|
|
4046c3176f | ||
|
|
c257fbfdcb | ||
|
|
1659be0d9f |
9
.github/workflows/cypress.yml
vendored
9
.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,7 +31,6 @@ jobs:
|
||||
wait-on-timeout: 120
|
||||
record: true
|
||||
parallel: true
|
||||
env: BASE_MODULE=mempool
|
||||
group: Tests on ${{ matrix.browser }} (Mempool)
|
||||
browser: ${{ matrix.browser }}
|
||||
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||
@@ -46,7 +51,6 @@ jobs:
|
||||
record: true
|
||||
parallel: true
|
||||
spec: cypress/integration/liquid/liquid.spec.ts
|
||||
env: BASE_MODULE=liquid
|
||||
group: Tests on ${{ matrix.browser }} (Liquid)
|
||||
browser: ${{ matrix.browser }}
|
||||
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||
@@ -67,7 +71,6 @@ jobs:
|
||||
record: true
|
||||
parallel: true
|
||||
spec: cypress/integration/bisq/bisq.spec.ts
|
||||
env: BASE_MODULE=bisq
|
||||
group: Tests on ${{ matrix.browser }} (Bisq)
|
||||
browser: ${{ matrix.browser }}
|
||||
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||
|
||||
40
README.md
40
README.md
@@ -1,8 +1,8 @@
|
||||
# The Mempool Open Source Project
|
||||
# The Mempool Open Source Project™
|
||||
|
||||
Mempool is the fully featured visualizer, explorer, and API service running on [mempool.space](https://mempool.space/), an open source project developed and operated for the benefit of the Bitcoin community, with a focus on the emerging transaction fee market to help our transition into a multi-layer ecosystem.
|
||||
|
||||

|
||||

|
||||
|
||||
## Installation Methods
|
||||
|
||||
@@ -20,11 +20,11 @@ The following instructions are for a manual installation on Linux or FreeBSD. Th
|
||||
|
||||
## 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 +41,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 +54,7 @@ Install MariaDB from OS package manager:
|
||||
|
||||
# macOS
|
||||
brew install mariadb
|
||||
brew services start mariadb
|
||||
mysql.server start
|
||||
```
|
||||
|
||||
Create database and grant privileges:
|
||||
@@ -80,7 +80,7 @@ Install mempool dependencies from npm and build the backend:
|
||||
```bash
|
||||
# backend
|
||||
cd backend
|
||||
npm install
|
||||
npm install --prod
|
||||
npm run build
|
||||
```
|
||||
|
||||
@@ -96,18 +96,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 +116,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 +156,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/mempool/ /var/www/
|
||||
```
|
||||
|
||||
## nginx + certbot
|
||||
@@ -176,7 +172,7 @@ Install the supplied nginx.conf and nginx-mempool.conf in /etc/nginx
|
||||
|
||||
```bash
|
||||
# install nginx and certbot
|
||||
apt-get install -y nginx python-certbot-nginx
|
||||
apt-get install -y nginx python3-certbot-nginx
|
||||
|
||||
# install the mempool configuration for nginx
|
||||
cp nginx.conf nginx-mempool.conf /etc/nginx/
|
||||
|
||||
519
backend/package-lock.json
generated
519
backend/package-lock.json
generated
@@ -11,22 +11,22 @@
|
||||
"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.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
|
||||
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
|
||||
"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.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
|
||||
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A=="
|
||||
},
|
||||
"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": {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -299,6 +299,11 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||
const witnessScript = vin.witness[vin.witness.length - 1];
|
||||
vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex')));
|
||||
}
|
||||
|
||||
if (vin.prevout.scriptpubkey_type === 'v1_p2tr' && vin.witness && vin.witness.length > 1) {
|
||||
const witnessScript = vin.witness[vin.witness.length - 2];
|
||||
vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex')));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,11 +87,8 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
||||
},
|
||||
'electrum': true,
|
||||
};
|
||||
} catch (e) {
|
||||
if (e === 'failed to get confirmed status') {
|
||||
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
||||
}
|
||||
throw new Error(typeof e === 'string' ? e : 'Error');
|
||||
} catch (e: any) {
|
||||
throw new Error(typeof e === 'string' ? e : e && e.message || e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +121,9 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
||||
}
|
||||
|
||||
return transactions;
|
||||
} catch (e) {
|
||||
} catch (e: any) {
|
||||
loadingIndicators.setProgress('address-' + address, 100);
|
||||
if (e === 'failed to get confirmed status') {
|
||||
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
||||
}
|
||||
throw new Error(typeof e === 'string' ? e : 'Error');
|
||||
throw new Error(typeof e === 'string' ? e : e && e.message || e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
178
backend/src/api/database-migration.ts
Normal file
178
backend/src/api/database-migration.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import config from '../config';
|
||||
import { DB } from '../database';
|
||||
import logger from '../logger';
|
||||
|
||||
class DatabaseMigration {
|
||||
private static currentVersion = 1;
|
||||
private queryTimeout = 120000;
|
||||
|
||||
constructor() { }
|
||||
|
||||
public async $initializeOrMigrateDatabase(): Promise<void> {
|
||||
if (!await this.$checkIfTableExists('statistics')) {
|
||||
await this.$initializeDatabaseTables();
|
||||
}
|
||||
|
||||
if (await this.$checkIfTableExists('state')) {
|
||||
const databaseSchemaVersion = await this.$getSchemaVersionFromDatabase();
|
||||
if (DatabaseMigration.currentVersion > databaseSchemaVersion) {
|
||||
await this.$migrateTableSchemaFromVersion(databaseSchemaVersion);
|
||||
}
|
||||
} else {
|
||||
await this.$migrateTableSchemaFromVersion(0);
|
||||
}
|
||||
}
|
||||
|
||||
private async $initializeDatabaseTables(): Promise<void> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
for (const query of this.getInitializeTableQueries()) {
|
||||
await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
}
|
||||
connection.release();
|
||||
logger.info(`Initial database tables have been created`);
|
||||
}
|
||||
|
||||
private async $migrateTableSchemaFromVersion(version: number): Promise<void> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
for (const query of this.getMigrationQueriesFromVersion(version)) {
|
||||
await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
}
|
||||
connection.release();
|
||||
await this.$updateToLatestSchemaVersion();
|
||||
logger.info(`Database schema have been migrated from version ${version} to ${DatabaseMigration.currentVersion} (latest version)`);
|
||||
}
|
||||
|
||||
private async $getSchemaVersionFromDatabase(): Promise<number> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `SELECT number FROM state WHERE name = 'schema_version';`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return rows[0]['number'];
|
||||
}
|
||||
|
||||
private async $updateToLatestSchemaVersion(): Promise<void> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `UPDATE state SET number = ${DatabaseMigration.currentVersion} WHERE name = 'schema_version'`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
}
|
||||
|
||||
private async $checkIfTableExists(table: string): Promise<boolean> {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${config.DATABASE.DATABASE}' AND TABLE_NAME = '${table}'`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return rows[0]['COUNT(*)'] === 1;
|
||||
}
|
||||
|
||||
private getInitializeTableQueries(): string[] {
|
||||
const queries: string[] = [];
|
||||
|
||||
queries.push(`CREATE TABLE IF NOT EXISTS statistics (
|
||||
id int(11) NOT NULL,
|
||||
added datetime NOT NULL,
|
||||
unconfirmed_transactions int(11) UNSIGNED NOT NULL,
|
||||
tx_per_second float UNSIGNED NOT NULL,
|
||||
vbytes_per_second int(10) UNSIGNED NOT NULL,
|
||||
mempool_byte_weight int(10) UNSIGNED NOT NULL,
|
||||
fee_data longtext NOT NULL,
|
||||
total_fee double UNSIGNED NOT NULL,
|
||||
vsize_1 int(11) NOT NULL,
|
||||
vsize_2 int(11) NOT NULL,
|
||||
vsize_3 int(11) NOT NULL,
|
||||
vsize_4 int(11) NOT NULL,
|
||||
vsize_5 int(11) NOT NULL,
|
||||
vsize_6 int(11) NOT NULL,
|
||||
vsize_8 int(11) NOT NULL,
|
||||
vsize_10 int(11) NOT NULL,
|
||||
vsize_12 int(11) NOT NULL,
|
||||
vsize_15 int(11) NOT NULL,
|
||||
vsize_20 int(11) NOT NULL,
|
||||
vsize_30 int(11) NOT NULL,
|
||||
vsize_40 int(11) NOT NULL,
|
||||
vsize_50 int(11) NOT NULL,
|
||||
vsize_60 int(11) NOT NULL,
|
||||
vsize_70 int(11) NOT NULL,
|
||||
vsize_80 int(11) NOT NULL,
|
||||
vsize_90 int(11) NOT NULL,
|
||||
vsize_100 int(11) NOT NULL,
|
||||
vsize_125 int(11) NOT NULL,
|
||||
vsize_150 int(11) NOT NULL,
|
||||
vsize_175 int(11) NOT NULL,
|
||||
vsize_200 int(11) NOT NULL,
|
||||
vsize_250 int(11) NOT NULL,
|
||||
vsize_300 int(11) NOT NULL,
|
||||
vsize_350 int(11) NOT NULL,
|
||||
vsize_400 int(11) NOT NULL,
|
||||
vsize_500 int(11) NOT NULL,
|
||||
vsize_600 int(11) NOT NULL,
|
||||
vsize_700 int(11) NOT NULL,
|
||||
vsize_800 int(11) NOT NULL,
|
||||
vsize_900 int(11) NOT NULL,
|
||||
vsize_1000 int(11) NOT NULL,
|
||||
vsize_1200 int(11) NOT NULL,
|
||||
vsize_1400 int(11) NOT NULL,
|
||||
vsize_1600 int(11) NOT NULL,
|
||||
vsize_1800 int(11) NOT NULL,
|
||||
vsize_2000 int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`);
|
||||
|
||||
queries.push(`ALTER TABLE statistics ADD PRIMARY KEY (id);`);
|
||||
queries.push(`ALTER TABLE statistics MODIFY id int(11) NOT NULL AUTO_INCREMENT;`);
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
private getMigrationQueriesFromVersion(version: number): string[] {
|
||||
const queries: string[] = [];
|
||||
|
||||
if (version < 1) {
|
||||
if (config.MEMPOOL.NETWORK !== 'liquid') {
|
||||
queries.push(`UPDATE statistics SET
|
||||
vsize_1 = vsize_1 + vsize_2, vsize_2 = vsize_3,
|
||||
vsize_3 = vsize_4, vsize_4 = vsize_5,
|
||||
vsize_5 = vsize_6, vsize_6 = vsize_8,
|
||||
vsize_8 = vsize_10, vsize_10 = vsize_12,
|
||||
vsize_12 = vsize_15, vsize_15 = vsize_20,
|
||||
vsize_20 = vsize_30, vsize_30 = vsize_40,
|
||||
vsize_40 = vsize_50, vsize_50 = vsize_60,
|
||||
vsize_60 = vsize_70, vsize_70 = vsize_80,
|
||||
vsize_80 = vsize_90, vsize_90 = vsize_100,
|
||||
vsize_100 = vsize_125, vsize_125 = vsize_150,
|
||||
vsize_150 = vsize_175, vsize_175 = vsize_200,
|
||||
vsize_200 = vsize_250, vsize_250 = vsize_300,
|
||||
vsize_300 = vsize_350, vsize_350 = vsize_400,
|
||||
vsize_400 = vsize_500, vsize_500 = vsize_600,
|
||||
vsize_600 = vsize_700, vsize_700 = vsize_800,
|
||||
vsize_800 = vsize_900, vsize_900 = vsize_1000,
|
||||
vsize_1000 = vsize_1200, vsize_1200 = vsize_1400,
|
||||
vsize_1400 = vsize_1800, vsize_1800 = vsize_2000, vsize_2000 = 0`);
|
||||
}
|
||||
|
||||
queries.push(`CREATE TABLE IF NOT EXISTS elements_pegs (
|
||||
block int(11) NOT NULL,
|
||||
datetime int(11) NOT NULL,
|
||||
amount bigint(20) NOT NULL,
|
||||
txid varchar(65) NOT NULL,
|
||||
txindex int(11) NOT NULL,
|
||||
bitcoinaddress varchar(100) NOT NULL,
|
||||
bitcointxid varchar(65) NOT NULL,
|
||||
bitcoinindex int(11) NOT NULL,
|
||||
final_tx int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`);
|
||||
|
||||
queries.push(`CREATE TABLE IF NOT EXISTS state (
|
||||
name varchar(25) NOT NULL,
|
||||
number int(11) NULL,
|
||||
string varchar(100) NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`);
|
||||
|
||||
queries.push(`INSERT INTO state VALUES('schema_version', 0, NULL);`);
|
||||
queries.push(`INSERT INTO state VALUES('last_elements_block', 0, NULL);`);
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
}
|
||||
|
||||
export default new DatabaseMigration();
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ import { DB } from '../database';
|
||||
import logger from '../logger';
|
||||
|
||||
import { Statistic, TransactionExtended, OptimizedStatistic } from '../mempool.interfaces';
|
||||
import config from '../config';
|
||||
|
||||
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': [],
|
||||
'24h': [], '1w': [], '1m': [], '3m': [], '6m': [], '1y': [], '2y': [], '3y': []
|
||||
};
|
||||
|
||||
public setNewStatisticsEntryCallback(fn: (stats: OptimizedStatistic) => void) {
|
||||
@@ -48,6 +49,8 @@ class Statistics {
|
||||
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');
|
||||
}
|
||||
|
||||
@@ -82,10 +85,15 @@ class Statistics {
|
||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||
|
||||
const weightVsizeFees: { [feePerWU: number]: number } = {};
|
||||
const lastItem = logFees.length - 1;
|
||||
|
||||
memPoolArray.forEach((transaction) => {
|
||||
for (let i = 0; i < logFees.length; i++) {
|
||||
if ((logFees[i] === 2000 && transaction.effectiveFeePerVsize >= 2000) || transaction.effectiveFeePerVsize <= logFees[i]) {
|
||||
if (
|
||||
(config.MEMPOOL.NETWORK === 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize * 10 < logFees[i + 1]))
|
||||
||
|
||||
(config.MEMPOOL.NETWORK !== 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize < logFees[i + 1]))
|
||||
) {
|
||||
if (weightVsizeFees[logFees[i]]) {
|
||||
weightVsizeFees[logFees[i]] += transaction.vsize;
|
||||
} else {
|
||||
@@ -259,8 +267,57 @@ class Statistics {
|
||||
}
|
||||
}
|
||||
|
||||
private getQueryForDays(div: number) {
|
||||
return `SELECT id, added, unconfirmed_transactions,
|
||||
private getQueryForDaysAvg(div: number, interval: string) {
|
||||
return `SELECT id, UNIX_TIMESTAMP(added) as added,
|
||||
CAST(avg(unconfirmed_transactions) as FLOAT) as unconfirmed_transactions,
|
||||
CAST(avg(tx_per_second) as FLOAT) as tx_per_second,
|
||||
CAST(avg(vbytes_per_second) as FLOAT) as vbytes_per_second,
|
||||
CAST(avg(vsize_1) as FLOAT) as vsize_1,
|
||||
CAST(avg(vsize_2) as FLOAT) as vsize_2,
|
||||
CAST(avg(vsize_3) as FLOAT) as vsize_3,
|
||||
CAST(avg(vsize_4) as FLOAT) as vsize_4,
|
||||
CAST(avg(vsize_5) as FLOAT) as vsize_5,
|
||||
CAST(avg(vsize_6) as FLOAT) as vsize_6,
|
||||
CAST(avg(vsize_8) as FLOAT) as vsize_8,
|
||||
CAST(avg(vsize_10) as FLOAT) as vsize_10,
|
||||
CAST(avg(vsize_12) as FLOAT) as vsize_12,
|
||||
CAST(avg(vsize_15) as FLOAT) as vsize_15,
|
||||
CAST(avg(vsize_20) as FLOAT) as vsize_20,
|
||||
CAST(avg(vsize_30) as FLOAT) as vsize_30,
|
||||
CAST(avg(vsize_40) as FLOAT) as vsize_40,
|
||||
CAST(avg(vsize_50) as FLOAT) as vsize_50,
|
||||
CAST(avg(vsize_60) as FLOAT) as vsize_60,
|
||||
CAST(avg(vsize_70) as FLOAT) as vsize_70,
|
||||
CAST(avg(vsize_80) as FLOAT) as vsize_80,
|
||||
CAST(avg(vsize_90) as FLOAT) as vsize_90,
|
||||
CAST(avg(vsize_100) as FLOAT) as vsize_100,
|
||||
CAST(avg(vsize_125) as FLOAT) as vsize_125,
|
||||
CAST(avg(vsize_150) as FLOAT) as vsize_150,
|
||||
CAST(avg(vsize_175) as FLOAT) as vsize_175,
|
||||
CAST(avg(vsize_200) as FLOAT) as vsize_200,
|
||||
CAST(avg(vsize_250) as FLOAT) as vsize_250,
|
||||
CAST(avg(vsize_300) as FLOAT) as vsize_300,
|
||||
CAST(avg(vsize_350) as FLOAT) as vsize_350,
|
||||
CAST(avg(vsize_400) as FLOAT) as vsize_400,
|
||||
CAST(avg(vsize_500) as FLOAT) as vsize_500,
|
||||
CAST(avg(vsize_600) as FLOAT) as vsize_600,
|
||||
CAST(avg(vsize_700) as FLOAT) as vsize_700,
|
||||
CAST(avg(vsize_800) as FLOAT) as vsize_800,
|
||||
CAST(avg(vsize_900) as FLOAT) as vsize_900,
|
||||
CAST(avg(vsize_1000) as FLOAT) as vsize_1000,
|
||||
CAST(avg(vsize_1200) as FLOAT) as vsize_1200,
|
||||
CAST(avg(vsize_1400) as FLOAT) as vsize_1400,
|
||||
CAST(avg(vsize_1600) as FLOAT) as vsize_1600,
|
||||
CAST(avg(vsize_1800) as FLOAT) as vsize_1800,
|
||||
CAST(avg(vsize_2000) as FLOAT) as vsize_2000 \
|
||||
FROM statistics \
|
||||
WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() \
|
||||
GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \
|
||||
ORDER BY id DESC;`;
|
||||
}
|
||||
|
||||
private getQueryForDays(div: number, interval: string) {
|
||||
return `SELECT id, UNIX_TIMESTAMP(added) as added, unconfirmed_transactions,
|
||||
tx_per_second,
|
||||
vbytes_per_second,
|
||||
vsize_1,
|
||||
@@ -300,13 +357,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 id DESC;`;
|
||||
}
|
||||
|
||||
public 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]) {
|
||||
@@ -320,7 +381,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 id DESC LIMIT 120`;
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -333,7 +394,7 @@ class Statistics {
|
||||
public async $list24H(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(180);
|
||||
const query = this.getQueryForDaysAvg(120, '1 DAY'); // 2m interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -346,7 +407,7 @@ class Statistics {
|
||||
public async $list1W(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(1260);
|
||||
const query = this.getQueryForDaysAvg(600, '1 WEEK'); // 10m interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -359,7 +420,7 @@ class Statistics {
|
||||
public async $list1M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(5040);
|
||||
const query = this.getQueryForDaysAvg(3600, '1 MONTH'); // 1h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -372,7 +433,7 @@ class Statistics {
|
||||
public async $list3M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(15120);
|
||||
const query = this.getQueryForDaysAvg(14400, '3 MONTH'); // 4h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -385,7 +446,7 @@ class Statistics {
|
||||
public async $list6M(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(30240);
|
||||
const query = this.getQueryForDaysAvg(21600, '6 MONTH'); // 6h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
@@ -398,15 +459,42 @@ class Statistics {
|
||||
public async $list1Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(60480);
|
||||
const query = this.getQueryForDays(43200, '1 YEAR'); // 12h interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
} catch (e) {
|
||||
logger.err('$list6M() error' + (e instanceof Error ? e.message : e));
|
||||
logger.err('$list1Y() error' + (e instanceof Error ? e.message : e));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async $list2Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(86400, "2 YEAR"); // 1d interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
} catch (e) {
|
||||
logger.err('$list2Y() error' + (e instanceof Error ? e.message : e));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async $list3Y(): Promise<OptimizedStatistic[]> {
|
||||
try {
|
||||
const connection = await DB.pool.getConnection();
|
||||
const query = this.getQueryForDays(86400, "3 YEAR"); // 1d interval
|
||||
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||
connection.release();
|
||||
return this.mapStatisticToOptimizedStatistic(rows);
|
||||
} catch (e) {
|
||||
logger.err('$list3Y() error' + (e instanceof Error ? e.message : e));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private mapStatisticToOptimizedStatistic(statistic: Statistic[]): OptimizedStatistic[] {
|
||||
return statistic.map((s) => {
|
||||
return {
|
||||
|
||||
@@ -21,6 +21,7 @@ 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';
|
||||
|
||||
class Server {
|
||||
private wss: WebSocket.Server | undefined;
|
||||
@@ -69,7 +70,8 @@ class Server {
|
||||
next();
|
||||
})
|
||||
.use(express.urlencoded({ extended: true }))
|
||||
.use(express.json());
|
||||
.use(express.text())
|
||||
;
|
||||
|
||||
this.server = http.createServer(this.app);
|
||||
this.wss = new WebSocket.Server({ server: this.server });
|
||||
@@ -80,6 +82,11 @@ class Server {
|
||||
|
||||
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) {
|
||||
@@ -142,7 +149,7 @@ class Server {
|
||||
if (this.wss) {
|
||||
websocketHandler.setWebsocketServer(this.wss);
|
||||
}
|
||||
if (config.MEMPOOL.NETWORK === 'liquid') {
|
||||
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||
blocks.setNewBlockCallback(async () => {
|
||||
try {
|
||||
await elementsParser.$parse();
|
||||
@@ -169,6 +176,7 @@ class Server {
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'backend-info', routes.getBackendInfo)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'init-data', routes.getInitData)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'validate-address/:address', routes.validateAddress)
|
||||
.post(config.MEMPOOL.API_URL_PREFIX + 'tx/push', routes.$postTransactionForm)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'donations', async (req, res) => {
|
||||
try {
|
||||
const response = await axios.get('https://mempool.space/api/v1/donations', { responseType: 'stream', timeout: 10000 });
|
||||
@@ -216,6 +224,8 @@ class Server {
|
||||
.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))
|
||||
;
|
||||
}
|
||||
|
||||
@@ -266,7 +276,7 @@ class Server {
|
||||
;
|
||||
}
|
||||
|
||||
if (config.MEMPOOL.NETWORK === 'liquid') {
|
||||
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||
this.app
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', routes.$getElementsPegsByMonth)
|
||||
;
|
||||
|
||||
@@ -52,6 +52,14 @@ class Routes {
|
||||
res.json(statistics.getCache()['1y']);
|
||||
}
|
||||
|
||||
public get2YStatistics(req: Request, res: Response) {
|
||||
res.json(statistics.getCache()['2y']);
|
||||
}
|
||||
|
||||
public get3YStatistics(req: Request, res: Response) {
|
||||
res.json(statistics.getCache()['3y']);
|
||||
}
|
||||
|
||||
public getInitData(req: Request, res: Response) {
|
||||
try {
|
||||
const result = websocketHandler.getInitData();
|
||||
@@ -613,7 +621,7 @@ class Routes {
|
||||
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
||||
res.json(addressData);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message && e.message.indexOf('exceeds') > 0) {
|
||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
@@ -630,7 +638,7 @@ class Routes {
|
||||
const transactions = await bitcoinApi.$getAddressTransactions(req.params.address, req.params.txId);
|
||||
res.json(transactions);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message && e.message.indexOf('exceeds') > 0) {
|
||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
res.status(500).send(e instanceof Error ? e.message : e);
|
||||
@@ -716,14 +724,16 @@ class Routes {
|
||||
const nextRetargetHeight = blockHeight + remainingBlocks;
|
||||
|
||||
let difficultyChange = 0;
|
||||
if (blocksInEpoch > 0) {
|
||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||
}
|
||||
if (difficultyChange > 300) {
|
||||
difficultyChange = 300;
|
||||
}
|
||||
if (difficultyChange < -75) {
|
||||
difficultyChange = -75;
|
||||
if (remainingBlocks < 1870) {
|
||||
if (blocksInEpoch > 0) {
|
||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||
}
|
||||
if (difficultyChange > 300) {
|
||||
difficultyChange = 300;
|
||||
}
|
||||
if (difficultyChange < -75) {
|
||||
difficultyChange = -75;
|
||||
}
|
||||
}
|
||||
|
||||
const timeAvgDiff = difficultyChange * 0.1;
|
||||
@@ -768,8 +778,29 @@ class Routes {
|
||||
public async $postTransaction(req: Request, res: Response) {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
try {
|
||||
const rawtx = Object.keys(req.body)[0];
|
||||
const txIdResult = await bitcoinApi.$sendRawTransaction(rawtx);
|
||||
let rawTx;
|
||||
if (typeof req.body === 'object') {
|
||||
rawTx = Object.keys(req.body)[0];
|
||||
} else {
|
||||
rawTx = req.body;
|
||||
}
|
||||
const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx);
|
||||
res.send(txIdResult);
|
||||
} catch (e: any) {
|
||||
res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||
: (e.message || 'Error'));
|
||||
}
|
||||
}
|
||||
|
||||
public async $postTransactionForm(req: Request, res: Response) {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
const matches = /tx=([a-z0-9]+)/.exec(req.body);
|
||||
let txHex = '';
|
||||
if (matches && matches[1]) {
|
||||
txHex = matches[1];
|
||||
}
|
||||
try {
|
||||
const txIdResult = await bitcoinClient.sendRawTransaction(txHex);
|
||||
res.send(txIdResult);
|
||||
} catch (e: any) {
|
||||
res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"lib": ["es2019"],
|
||||
"lib": ["es2019", "dom"],
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:12-buster-slim AS builder
|
||||
FROM node:16.10.0-buster-slim AS builder
|
||||
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
@@ -8,7 +8,7 @@ RUN apt-get install -y build-essential python3 pkg-config
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
FROM node:12-buster-slim
|
||||
FROM node:16.10.0-buster-slim
|
||||
|
||||
WORKDIR /backend
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:12-buster-slim AS builder
|
||||
FROM node:16.10.0-buster-slim AS builder
|
||||
|
||||
ARG commitHash
|
||||
ENV DOCKER_COMMIT_HASH=${commitHash}
|
||||
|
||||
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@@ -34,6 +34,7 @@ speed-measure-plugin.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
|
||||
@@ -1,11 +1,50 @@
|
||||
# mempool-frontend
|
||||
|
||||
## Transifex Project
|
||||
## Contributing
|
||||
|
||||
This package is used for the https://mempool.space, https://liquid.network and https://bisq.markets websites - there are npm scripts to setup all three, which effectively change how BASE_MODULE is configured:
|
||||
|
||||
```
|
||||
$ npm run config:defaults:mempool
|
||||
$ npm run config:defaults:liquid
|
||||
$ npm run config:defaults:bisq
|
||||
```
|
||||
|
||||
Changes that affect the frontend codebase only can be done using the production backend so you don't need to spin up the entire Mempool infrastructure. This is very convenient in case you want to quickly improve the UI, fix typos or implement new features that don't require any backend changes.
|
||||
|
||||
Make your changes, install the project dependencies and run the frontend server as follows:
|
||||
|
||||
```
|
||||
$ npm install
|
||||
$ npm run serve:local-prod
|
||||
```
|
||||
|
||||
The frontend will be available at http://localhost:4200/ and all API requests will be proxied to the production server at https://mempool.space
|
||||
|
||||
After making your changes, you can run our end-to-end automation suite and check for possible regressions:
|
||||
|
||||
Headless:
|
||||
|
||||
```
|
||||
$ npm run config:defaults:mempool && npm run cypress:run
|
||||
```
|
||||
|
||||
Interactive:
|
||||
|
||||
```
|
||||
$ npm run config:defaults:mempool && npm run cypress:open
|
||||
```
|
||||
|
||||
This will open the Cypress test runner, where you can select any of the test files to run.
|
||||
|
||||
If all tests are green, submit your PR and it will be reviewed by someone on the team as soon as possible.
|
||||
|
||||
## Translations: Transifex Project
|
||||
|
||||
The mempool frontend strings are localized into 20+ locales:
|
||||
https://www.transifex.com/mempool/mempool/dashboard/
|
||||
|
||||
## Translators
|
||||
### Translators
|
||||
|
||||
* Arabic @baro0k
|
||||
* Czech @pixelmade2
|
||||
@@ -27,10 +66,11 @@ https://www.transifex.com/mempool/mempool/dashboard/
|
||||
* Slovenian @thepkbadger
|
||||
* Finnish @bio_bitcoin
|
||||
* Swedish @softsimon_
|
||||
* Thai @Gusb3ll
|
||||
* Turkish @stackmore
|
||||
* Ukrainian @volbil
|
||||
* Vietnamese @bitcoin_vietnam
|
||||
* Chinese @wdljt
|
||||
* Russian @TonyCrusoe @Bitconan
|
||||
* Romanian @mirceavesa
|
||||
* Macedonian @SkechBoy
|
||||
* Macedonian @SkechBoy
|
||||
|
||||
@@ -94,6 +94,10 @@
|
||||
"translation": "src/locale/messages.sv.xlf",
|
||||
"baseHref": "/sv/"
|
||||
},
|
||||
"th": {
|
||||
"translation": "src/locale/messages.th.xlf",
|
||||
"baseHref": "/th/"
|
||||
},
|
||||
"tr": {
|
||||
"translation": "src/locale/messages.tr.xlf",
|
||||
"baseHref": "/tr/"
|
||||
@@ -211,11 +215,11 @@
|
||||
"browserTarget": "mempool:build:production"
|
||||
},
|
||||
"local": {
|
||||
"proxyConfig": "proxy.conf.json",
|
||||
"proxyConfig": "proxy.conf.local.js",
|
||||
"verbose": true
|
||||
},
|
||||
"staging": {
|
||||
"proxyConfig": "proxy.stg.conf.json",
|
||||
"proxyConfig": "proxy.conf.js",
|
||||
"disableHostCheck": true,
|
||||
"host": "0.0.0.0",
|
||||
"verbose": true
|
||||
@@ -251,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,8 +1,8 @@
|
||||
describe('Bisq', () => {
|
||||
let baseModule;
|
||||
const baseModule = Cypress.env("BASE_MODULE");
|
||||
const basePath = (baseModule === 'bisq') ? '' : '/bisq';
|
||||
|
||||
beforeEach(() => {
|
||||
baseModule = (Cypress.env('BASE_MODULE') && Cypress.env('BASE_MODULE') === 'bisq') ? '' : '/bisq';
|
||||
|
||||
cy.intercept('/sockjs-node/info*').as('socket');
|
||||
cy.intercept('/bisq/api/markets/hloc?market=btc_usd&interval=day').as('hloc');
|
||||
cy.intercept('/bisq/api/markets/ticker').as('ticker');
|
||||
@@ -23,15 +23,15 @@ describe('Bisq', () => {
|
||||
});
|
||||
});
|
||||
|
||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") !== 'liquid') {
|
||||
if (baseModule === 'mempool' || baseModule === 'bisq') {
|
||||
|
||||
it('loads the dashboard', () => {
|
||||
cy.visit(`${baseModule}`);
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('loads the transactions screen', () => {
|
||||
cy.visit(`${baseModule}`);
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
||||
cy.get('.table > tr').should('have.length', 50);
|
||||
@@ -39,7 +39,7 @@ describe('Bisq', () => {
|
||||
});
|
||||
|
||||
it('loads the blocks screen', () => {
|
||||
cy.visit(`${baseModule}`);
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||
cy.wait('@blocks');
|
||||
@@ -48,7 +48,7 @@ describe('Bisq', () => {
|
||||
});
|
||||
|
||||
it('loads the stats screen', () => {
|
||||
cy.visit(`${baseModule}`);
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||
cy.wait('@stats');
|
||||
@@ -56,7 +56,7 @@ describe('Bisq', () => {
|
||||
});
|
||||
|
||||
it('loads the api screen', () => {
|
||||
cy.visit(`${baseModule}`);
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('li:nth-of-type(5) > a').click().then(() => {
|
||||
cy.get('.card').should('have.length.at.least', 1);
|
||||
@@ -67,7 +67,7 @@ describe('Bisq', () => {
|
||||
|
||||
it('shows blocks pagination with 5 pages (desktop)', () => {
|
||||
cy.viewport(760, 800);
|
||||
cy.visit(`${baseModule}/blocks`);
|
||||
cy.visit(`${basePath}/blocks`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('tbody tr').should('have.length', 10);
|
||||
// 5 pages + 4 buttons = 9 buttons
|
||||
@@ -76,13 +76,13 @@ describe('Bisq', () => {
|
||||
|
||||
it('shows blocks pagination with 3 pages (mobile)', () => {
|
||||
cy.viewport(669, 800);
|
||||
cy.visit(`${baseModule}/blocks`);
|
||||
cy.visit(`${basePath}/blocks`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('tbody tr').should('have.length', 10);
|
||||
// 3 pages + 4 buttons = 7 buttons
|
||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
||||
});
|
||||
} else {
|
||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
||||
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
describe('Liquid', () => {
|
||||
let baseModule;
|
||||
beforeEach(() => {
|
||||
baseModule = (Cypress.env('BASE_MODULE') && Cypress.env('BASE_MODULE') === 'liquid') ? '' : '/liquid';
|
||||
const baseModule = Cypress.env("BASE_MODULE");
|
||||
const basePath = (baseModule === 'liquid') ? '' : '/liquid';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('/liquid/api/block/**').as('block');
|
||||
cy.intercept('/liquid/api/blocks/').as('blocks');
|
||||
cy.intercept('/liquid/api/tx/**/outspends').as('outspends');
|
||||
@@ -16,30 +16,36 @@ describe('Liquid', () => {
|
||||
});
|
||||
});
|
||||
|
||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") !== 'bisq') {
|
||||
if (baseModule === 'mempool' || baseModule === 'liquid') {
|
||||
|
||||
it('check first mempool block after skeleton loads', () => {
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||
});
|
||||
|
||||
it('loads the dashboard', () => {
|
||||
cy.visit(`${baseModule}`);
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('loads the blocks page', () => {
|
||||
cy.visit(`${baseModule}/blocks`);
|
||||
cy.visit(`${basePath}/blocks`);
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('loads a specific block page', () => {
|
||||
cy.visit(`${baseModule}/block/7e1369a23a5ab861e7bdede2aadcccae4ea873ffd9caf11c7c5541eb5bcdff54`);
|
||||
cy.visit(`${basePath}/block/7e1369a23a5ab861e7bdede2aadcccae4ea873ffd9caf11c7c5541eb5bcdff54`);
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('loads the graphs page', () => {
|
||||
cy.visit(`${baseModule}/graphs`);
|
||||
cy.visit(`${basePath}/graphs`);
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('loads the tv page - desktop', () => {
|
||||
cy.visit(`${baseModule}`);
|
||||
cy.visit(`${basePath}`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||
cy.wait(1000);
|
||||
@@ -47,7 +53,7 @@ describe('Liquid', () => {
|
||||
});
|
||||
|
||||
it('loads the graphs page - mobile', () => {
|
||||
cy.visit(`${baseModule}`)
|
||||
cy.visit(`${basePath}`)
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||
cy.viewport('iphone-6');
|
||||
@@ -56,15 +62,63 @@ 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(`${baseModule}/assets`);
|
||||
cy.visit(`${basePath}/assets`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('table tr').should('have.length.at.least', 5);
|
||||
});
|
||||
|
||||
it('allows searching assets', () => {
|
||||
cy.visit(`${baseModule}/assets`);
|
||||
cy.visit(`${basePath}/assets`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.container-xl input').click().type('Liquid Bitcoin').then(() => {
|
||||
cy.get('table tr').should('have.length', 1);
|
||||
@@ -72,7 +126,7 @@ describe('Liquid', () => {
|
||||
});
|
||||
|
||||
it('shows a specific asset ID', () => {
|
||||
cy.visit(`${baseModule}/assets`);
|
||||
cy.visit(`${basePath}/assets`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.container-xl input').click().type('Liquid AUD').then(() => {
|
||||
cy.get('table tr td:nth-of-type(1) a').click();
|
||||
@@ -84,27 +138,27 @@ describe('Liquid', () => {
|
||||
describe('unblinded TX', () => {
|
||||
|
||||
it('should not show an unblinding error message for regular txs', () => {
|
||||
cy.visit(`${baseModule}/tx/82a479043ec3841e0d3f829afc8df4f0e2bbd675a13f013ea611b2fde0027d45`);
|
||||
cy.visit(`${basePath}/tx/82a479043ec3841e0d3f829afc8df4f0e2bbd675a13f013ea611b2fde0027d45`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.error-unblinded' ).should('not.exist');
|
||||
});
|
||||
|
||||
it('show unblinded TX', () => {
|
||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#table-tx-vin tr').should('have.class', 'assetBox');
|
||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||
});
|
||||
|
||||
it('show empty unblinded TX', () => {
|
||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=`);
|
||||
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#table-tx-vin tr').should('have.class', '');
|
||||
cy.get('#table-tx-vout tr').should('have.class', '');
|
||||
});
|
||||
|
||||
it('show invalid unblinded TX hex', () => {
|
||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=123`);
|
||||
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=123`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#table-tx-vin tr').should('have.class', '');
|
||||
cy.get('#table-tx-vout tr').should('have.class', '');
|
||||
@@ -112,36 +166,36 @@ describe('Liquid', () => {
|
||||
});
|
||||
|
||||
it('show first unblinded vout', () => {
|
||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc`);
|
||||
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#table-tx-vout tr:first-child()').should('have.class', 'assetBox');
|
||||
});
|
||||
|
||||
it('show second unblinded vout', () => {
|
||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||
});
|
||||
|
||||
it('show invalid error unblinded TX', () => {
|
||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3c`);
|
||||
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3c`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||
cy.get('.error-unblinded' ).contains('Error: Invalid blinding data.');
|
||||
});
|
||||
|
||||
it('shows asset peg in/out and burn transactions', () => {
|
||||
cy.visit(`${baseModule}/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`);
|
||||
cy.visit(`${basePath}/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`);
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#table-tx-vout tr').not('.assetBox');
|
||||
cy.get('#table-tx-vin tr').not('.assetBox');
|
||||
});
|
||||
|
||||
it('prevents regressing issue #644', () => {
|
||||
cy.visit(`${baseModule}/tx/393b890966f305e7c440fcfb12a13f51a7a9011cc59ff5f14f6f93214261bd82`);
|
||||
cy.visit(`${basePath}/tx/393b890966f305e7c440fcfb12a13f51a7a9011cc59ff5f14f6f93214261bd82`);
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
||||
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
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');
|
||||
@@ -20,7 +52,13 @@ describe('Mainnet', () => {
|
||||
});
|
||||
});
|
||||
|
||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") === 'mempool') {
|
||||
if (baseModule === 'mempool') {
|
||||
|
||||
it('check first mempool block after skeleton loads', () => {
|
||||
cy.visit('/');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||
});
|
||||
|
||||
it('loads the status screen', () => {
|
||||
cy.visit('/status');
|
||||
@@ -48,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');
|
||||
@@ -61,6 +99,22 @@ describe('Mainnet', () => {
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('check op_return tx tooltip', () => {
|
||||
cy.visit('/block/00000000000000000003c5f542bed265319c6cf64238cf1f1bb9bca3ebf686d2');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('tbody > :nth-child(2) > :nth-child(1) > a').first().trigger('onmouseover');
|
||||
cy.get('tbody > :nth-child(2) > :nth-child(1) > a').first().trigger('mouseenter');
|
||||
cy.get('.tooltip-inner').should('be.visible');
|
||||
});
|
||||
|
||||
it('check op_return coinbase tooltip', () => {
|
||||
cy.visit('/block/00000000000000000003c5f542bed265319c6cf64238cf1f1bb9bca3ebf686d2');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('div > a > .badge').first().trigger('onmouseover');
|
||||
cy.get('div > a > .badge').first().trigger('mouseenter');
|
||||
cy.get('.tooltip-inner').should('be.visible');
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
it('allows searching for partial Bitcoin addresses', () => {
|
||||
cy.visit('/');
|
||||
@@ -87,18 +141,33 @@ describe('Mainnet', () => {
|
||||
});
|
||||
|
||||
['BC1PQYQSZQ', 'bc1PqYqSzQ'].forEach((searchTerm) => {
|
||||
it(`allows searching for partial case insensitive bc1 addresses: ${searchTerm}`, () => {
|
||||
it(`allows searching for partial case insensitive bech32m addresses: ${searchTerm}`, () => {
|
||||
cy.visit('/');
|
||||
cy.get('.search-box-container > .form-control').type(searchTerm).then(() => {
|
||||
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 1);
|
||||
cy.get('ngb-typeahead-window button.dropdown-item.active').click().then(() => {
|
||||
cy.url().should('include', '/address/bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3wf0qm');
|
||||
cy.url().should('include', '/address/bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsyjer9e');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
['BC1Q000375VXCU', 'bC1q000375vXcU'].forEach((searchTerm) => {
|
||||
it(`allows searching for partial case insensitive bech32 addresses: ${searchTerm}`, () => {
|
||||
cy.visit('/');
|
||||
cy.get('.search-box-container > .form-control').type(searchTerm).then(() => {
|
||||
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 1);
|
||||
cy.get('ngb-typeahead-window button.dropdown-item.active').click().then(() => {
|
||||
cy.url().should('include', '/address/bc1q000375vxcuf5v04lmwy22vy2thvhqkxghgq7dy');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('blocks navigation', () => {
|
||||
@@ -232,7 +301,7 @@ describe('Mainnet', () => {
|
||||
cy.changeNetwork("bisq");
|
||||
});
|
||||
|
||||
it('loads the dashboard with the skeleton blocks', () => {
|
||||
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||
cy.mockMempoolSocket();
|
||||
cy.visit("/");
|
||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||
@@ -244,7 +313,7 @@ describe('Mainnet', () => {
|
||||
|
||||
emitMempoolInfo({
|
||||
'params': {
|
||||
loaded: true
|
||||
command: 'init'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -269,6 +338,33 @@ describe('Mainnet', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('graphs page', () => {
|
||||
it('check buttons - mobile', () => {
|
||||
cy.viewport('iphone-6');
|
||||
cy.visit('/graphs');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.small-buttons > :nth-child(2)').should('be.visible');
|
||||
cy.get('#dropdownFees').should('be.visible');
|
||||
cy.get('.btn-group').should('be.visible');
|
||||
});
|
||||
it('check buttons - tablet', () => {
|
||||
cy.viewport('ipad-2');
|
||||
cy.visit('/graphs');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.small-buttons > :nth-child(2)').should('be.visible');
|
||||
cy.get('#dropdownFees').should('be.visible');
|
||||
cy.get('.btn-group').should('be.visible');
|
||||
});
|
||||
it('check buttons - desktop', () => {
|
||||
cy.viewport('macbook-16');
|
||||
cy.visit('/graphs');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('.small-buttons > :nth-child(2)').should('be.visible');
|
||||
cy.get('#dropdownFees').should('be.visible');
|
||||
cy.get('.btn-group').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('loads the tv screen - desktop', () => {
|
||||
cy.viewport('macbook-16');
|
||||
cy.visit('/');
|
||||
@@ -281,7 +377,7 @@ describe('Mainnet', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.only('loads the tv screen - mobile', () => {
|
||||
it('loads the tv screen - mobile', () => {
|
||||
cy.viewport('iphone-6');
|
||||
cy.visit('/tv');
|
||||
cy.waitForSkeletonGone();
|
||||
@@ -362,7 +458,79 @@ 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");
|
||||
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { emitMempoolInfo } from "../../support/websocket";
|
||||
|
||||
const baseModule = Cypress.env("BASE_MODULE");
|
||||
|
||||
describe('Signet', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('/api/block-height/*').as('block-height');
|
||||
@@ -9,13 +11,19 @@ describe('Signet', () => {
|
||||
});
|
||||
|
||||
|
||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") === 'mempool') {
|
||||
if (baseModule === 'mempool') {
|
||||
it('loads the dashboard', () => {
|
||||
cy.visit('/signet');
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('loads the dashboard with the skeleton blocks', () => {
|
||||
it('check first mempool block after skeleton loads', () => {
|
||||
cy.visit('/');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||
});
|
||||
|
||||
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||
cy.mockMempoolSocket();
|
||||
cy.visit("/signet");
|
||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||
@@ -126,6 +134,6 @@ describe('Signet', () => {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
||||
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { confirmAddress, emitMempoolInfo, sendWsMock, showNewTx, startTrackingAddress } from "../../support/websocket";
|
||||
|
||||
const baseModule = Cypress.env("BASE_MODULE");
|
||||
|
||||
describe('Testnet', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('/api/block-height/*').as('block-height');
|
||||
@@ -8,14 +10,20 @@ describe('Testnet', () => {
|
||||
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
||||
});
|
||||
|
||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") === 'mempool') {
|
||||
if (baseModule === 'mempool') {
|
||||
|
||||
it('loads the dashboard', () => {
|
||||
cy.visit('/testnet');
|
||||
cy.waitForSkeletonGone();
|
||||
});
|
||||
|
||||
it('loads the dashboard with the skeleton blocks', () => {
|
||||
it('check first mempool block after skeleton loads', () => {
|
||||
cy.visit('/');
|
||||
cy.waitForSkeletonGone();
|
||||
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||
});
|
||||
|
||||
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||
cy.mockMempoolSocket();
|
||||
cy.visit("/testnet");
|
||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||
@@ -123,6 +131,6 @@ describe('Testnet', () => {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
||||
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
module.exports = (on, config) => {}
|
||||
const fs = require('fs');
|
||||
|
||||
const CONFIG_FILE = 'mempool-frontend-config.json';
|
||||
|
||||
module.exports = (on, config) => {
|
||||
if (fs.existsSync(CONFIG_FILE)) {
|
||||
let contents = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
||||
config.env.BASE_MODULE = contents.BASE_MODULE ? contents.BASE_MODULE : 'mempool';
|
||||
} else {
|
||||
config.env.BASE_MODULE = 'mempool';
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
15408
frontend/package-lock.json
generated
15408
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -53,16 +53,18 @@
|
||||
"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.0.4",
|
||||
"@angular/animations": "~13.0.3",
|
||||
"@angular/cli": "~13.0.4",
|
||||
"@angular/common": "~13.0.3",
|
||||
"@angular/compiler": "~13.0.3",
|
||||
"@angular/core": "~13.0.3",
|
||||
"@angular/forms": "~13.0.3",
|
||||
"@angular/localize": "^13.0.3",
|
||||
"@angular/platform-browser": "~13.0.3",
|
||||
"@angular/platform-browser-dynamic": "~13.0.3",
|
||||
"@angular/platform-server": "~13.0.3",
|
||||
"@angular/router": "~13.0.3",
|
||||
"@fortawesome/angular-fontawesome": "^0.8.2",
|
||||
"@fortawesome/fontawesome-common-types": "^0.2.35",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
||||
@@ -71,7 +73,7 @@
|
||||
"@mempool/mempool.js": "^2.2.4",
|
||||
"@ng-bootstrap/ng-bootstrap": "^10.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 +84,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 +92,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.0.3",
|
||||
"@angular/language-service": "~13.0.3",
|
||||
"@nguniversal/builders": "^11.2.1",
|
||||
"@types/express": "^4.17.0",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
@@ -110,14 +110,14 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ try {
|
||||
throw new Error(e);
|
||||
} else {
|
||||
console.log(`${CONFIG_FILE_NAME} file not found, using default config`);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,4 +60,20 @@ PROXY_CONFIG = [
|
||||
}
|
||||
];
|
||||
|
||||
if (configContent && configContent.BASE_MODULE == "liquid") {
|
||||
PROXY_CONFIG.push({
|
||||
context: ['/resources/pools.json', '/resources/assets.json', '/resources/assets.minimal.json'],
|
||||
target: "https://liquid.network",
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
});
|
||||
} else {
|
||||
PROXY_CONFIG.push({
|
||||
context: ['/resources/pools.json', '/resources/assets.json', '/resources/assets.minimal.json'],
|
||||
target: "https://mempool.space",
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = PROXY_CONFIG;
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
{
|
||||
"/api/v1": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false
|
||||
},
|
||||
"/api/v1/ws": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"ws": true
|
||||
},
|
||||
"/api/": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/api/": "/api/v1/"
|
||||
}
|
||||
},
|
||||
"/testnet/api/v1": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/testnet/api/v1": "/api/v1"
|
||||
}
|
||||
},
|
||||
"/testnet/api/v1/ws": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/testnet/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/testnet/api/": {
|
||||
"target": "http://localhost:50001/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/testnet/api": ""
|
||||
}
|
||||
},
|
||||
"/signet/api/v1": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/signet/api/v1": "/api/v1"
|
||||
}
|
||||
},
|
||||
"/signet/api/v1/ws": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/signet/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/signet/api/": {
|
||||
"target": "http://localhost:50001/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/signet/api": ""
|
||||
}
|
||||
},
|
||||
"/liquid/api/v1/ws": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/liquid/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/liquid/api/v1/": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/liquid/api/": "/api/"
|
||||
}
|
||||
},
|
||||
"/liquid/api/": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/liquid/api/": "/api/v1/"
|
||||
}
|
||||
},
|
||||
"/bisq/api/": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/bisq/api/": "/api/v1/bisq/"
|
||||
}
|
||||
},
|
||||
"/bisq/api/v1/ws": {
|
||||
"target": "http://localhost:8999/",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/bisq/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/resources/assets.minimal.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
},
|
||||
"/resources/assets.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
},
|
||||
"/resources/pools.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
}
|
||||
}
|
||||
73
frontend/proxy.conf.local.js
Normal file
73
frontend/proxy.conf.local.js
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
let PROXY_CONFIG = require('./proxy.conf.js');
|
||||
const BACKEND_CONFIG_FILE_NAME = '../backend/mempool-config.json';
|
||||
const FRONTEND_CONFIG_FILE_NAME = 'mempool-frontend-config.json';
|
||||
|
||||
let backendConfigContent;
|
||||
let frontendConfigContent;
|
||||
|
||||
// Read frontend config
|
||||
try {
|
||||
const rawConfig = fs.readFileSync(FRONTEND_CONFIG_FILE_NAME);
|
||||
frontendConfigContent = JSON.parse(rawConfig);
|
||||
console.log(`${FRONTEND_CONFIG_FILE_NAME} file found, using provided config`);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
if (e.code !== 'ENOENT') {
|
||||
throw new Error(e);
|
||||
} else {
|
||||
console.log(`${FRONTEND_CONFIG_FILE_NAME} file not found, using default config`);
|
||||
}
|
||||
}
|
||||
|
||||
// Read backend config
|
||||
try {
|
||||
const rawConfig = fs.readFileSync(BACKEND_CONFIG_FILE_NAME);
|
||||
backendConfigContent = JSON.parse(rawConfig);
|
||||
console.log(`${BACKEND_CONFIG_FILE_NAME} file found, using provided config`);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
if (e.code !== 'ENOENT') {
|
||||
throw new Error(e);
|
||||
} else {
|
||||
console.log(`${BACKEND_CONFIG_FILE_NAME} file not found, using default config`);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the "/api/**" entry from the default proxy config
|
||||
let localDevContext = PROXY_CONFIG[0].context
|
||||
|
||||
localDevContext.splice(PROXY_CONFIG[0].context.indexOf('/api/**'), 1);
|
||||
|
||||
PROXY_CONFIG[0].context = localDevContext;
|
||||
|
||||
// Change all targets to localhost
|
||||
PROXY_CONFIG.map(conf => conf.target = "http://localhost:8999");
|
||||
|
||||
// Add rules for local backend
|
||||
if (backendConfigContent) {
|
||||
PROXY_CONFIG.push({
|
||||
context: ['/api/address/**', '/api/tx/**', '/api/block/**', '/api/blocks/**'],
|
||||
target: `http://localhost:8999`,
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
proxyTimeout: 30000,
|
||||
pathRewrite: {
|
||||
"^/api/": "/api/v1/"
|
||||
},
|
||||
});
|
||||
PROXY_CONFIG.push({
|
||||
context: ['/api/v1/**'],
|
||||
target: `http://localhost:8999`,
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
proxyTimeout: 30000
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
console.log(PROXY_CONFIG);
|
||||
|
||||
module.exports = PROXY_CONFIG;
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"/api/v1/ws": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"ws": true
|
||||
},
|
||||
"/api": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug",
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/testnet/api/v1/ws": {
|
||||
"target": "https://mempool.space/testnet",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"loglevel": "debug",
|
||||
"pathRewrite": {
|
||||
"^/testnet/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/testnet/api": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": true,
|
||||
"changeOrigin": true,
|
||||
"loglevel": "debug",
|
||||
"pathRewrite": {
|
||||
"/testnet/api": "/testnet/api"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/signet/api/v1/ws": {
|
||||
"target": "https://mempool.space/signet",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"loglevel": "debug",
|
||||
"pathRewrite": {
|
||||
"^/signet/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/signet/api": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": true,
|
||||
"changeOrigin": true,
|
||||
"loglevel": "debug",
|
||||
"pathRewrite": {
|
||||
"/signet/api": "/signet/api"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
|
||||
"/bisq/api/v1/ws": {
|
||||
"target": "https://mempool.space/bisq",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/bisq/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/bisq/api": {
|
||||
"target": "https://mempool.space/bisq",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/bisq/api/": "/api/v1/bisq/"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/liquid/api/v1/ws": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"ws": true
|
||||
},
|
||||
"/liquid/api": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/liquid/api/": "/liquid/api/"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/resources/assets.minimal.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
},
|
||||
"/resources/assets.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
},
|
||||
"/resources/pools.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
{
|
||||
"/api/v1/ws": {
|
||||
"target": "https://mempool.ninja",
|
||||
"secure": false,
|
||||
"ws": true
|
||||
},
|
||||
"/api/*": {
|
||||
"target": "https://mempool.ninja",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug",
|
||||
"pathRewrite": {
|
||||
"^/api": "https://mempool.ninja/api"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/testnet/api/v1/ws": {
|
||||
"target": "https://mempool.ninja/testnet",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/testnet/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/testnet/api/v1/*": {
|
||||
"target": "https://mempool.ninja/testnet",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/testnet/api/v1": "/api/v1"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/signet/api/v1/ws": {
|
||||
"target": "https://mempool.ninja/signet",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/signet/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/signet/api/v1/*": {
|
||||
"target": "https://mempool.ninja/signet",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/signet/api/v1": "/api/v1"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/bisq/api/v1/ws": {
|
||||
"target": "https://mempool.ninja/bisq",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/bisq/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/bisq/api/*": {
|
||||
"target": "https://mempool.ninja/bisq",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/bisq/api/": "/api/v1/bisq/"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/liquid/api/v1/ws": {
|
||||
"target": "https://mempool.ninja/liquid",
|
||||
"secure": false,
|
||||
"ws": true,
|
||||
"pathRewrite": {
|
||||
"^/liquid/api": "/api/v1/ws"
|
||||
}
|
||||
},
|
||||
"/liquid/api/*": {
|
||||
"target": "https://mempool.ninja/liquid",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": {
|
||||
"^/liquid/api/": "/api/liquid/"
|
||||
},
|
||||
"timeout": 3600000
|
||||
},
|
||||
"/resources/assets.minimal.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
},
|
||||
"/resources/assets.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
},
|
||||
"/resources/pools.json": {
|
||||
"target": "https://mempool.space",
|
||||
"secure": false,
|
||||
"changeOrigin": true
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,24 @@ 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';
|
||||
import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component';
|
||||
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
|
||||
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
|
||||
|
||||
let routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
@@ -61,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',
|
||||
@@ -95,6 +112,10 @@ let routes: Routes = [
|
||||
path: '',
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
@@ -137,9 +158,21 @@ let routes: Routes = [
|
||||
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'
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -164,6 +197,10 @@ let routes: Routes = [
|
||||
path: '',
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
@@ -199,9 +236,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'
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -226,6 +275,10 @@ let routes: Routes = [
|
||||
path: '',
|
||||
component: MasterPageComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
@@ -261,9 +314,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'
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -325,6 +390,10 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
path: '',
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'tx/:id',
|
||||
component: TransactionComponent
|
||||
@@ -359,9 +428,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',
|
||||
@@ -408,3 +489,4 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
|
||||
|
||||
@@ -123,9 +123,20 @@ export const languages: Language[] = [
|
||||
// { code: 'sh', name: 'Srpskohrvatski / српскохрватски' },// Serbo-Croatian
|
||||
{ code: 'fi', name: 'Suomi' }, // Finnish
|
||||
{ code: 'sv', name: 'Svenska' }, // Swedish
|
||||
// { code: 'th', name: 'ไทย' }, // Thai
|
||||
{ code: 'th', name: 'ไทย' }, // Thai
|
||||
{ code: 'tr', name: 'Türkçe' }, // Turkish
|
||||
{ code: 'uk', name: 'Українська' }, // Ukrainian
|
||||
{ code: 'vi', name: 'Tiếng Việt' }, // Vietnamese
|
||||
{ code: 'zh', name: '中文' }, // Chinese
|
||||
];
|
||||
|
||||
export const specialBlocks = {
|
||||
'709632': {
|
||||
labelEvent: 'Taproot 🌱 activation',
|
||||
labelEventCompleted: 'Taproot 🌱 has been activated!',
|
||||
},
|
||||
'840000': {
|
||||
labelEvent: 'Halving 🥳',
|
||||
labelEventCompleted: 'Block Subsidy has halved to 3.125 BTC per block',
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,15 +48,17 @@ 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 } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ApiDocsComponent } from './components/docs/api-docs.component';
|
||||
import { DocsComponent } from './components/docs/docs.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 { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -98,6 +100,8 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||
PrivacyPolicyComponent,
|
||||
TrademarkPolicyComponent,
|
||||
SponsorComponent,
|
||||
PushTransactionComponent,
|
||||
DocsComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
@@ -156,5 +160,6 @@ export class AppModule {
|
||||
library.addIcons(faCaretDown);
|
||||
library.addIcons(faAngleRight);
|
||||
library.addIcons(faAngleLeft);
|
||||
library.addIcons(faBook);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<div class="container-xl">
|
||||
<h1 style="float: left;" i18n="Registered assets page header">Registered assets</h1>
|
||||
<br>
|
||||
|
||||
<div class="title-asset">
|
||||
<h1 i18n="Registered assets page header">Registered assets</h1>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<form [formGroup]="searchForm" class="form-inline">
|
||||
<div class="input-group m-2">
|
||||
<div class="input-group mb-2">
|
||||
<input style="width: 250px;" formControlName="searchText" type="text" class="form-control" i18n-placeholder="Search Assets Placeholder Text" placeholder="Search asset">
|
||||
<div class="input-group-append">
|
||||
<button [disabled]="!searchForm.get('searchText')?.value.length" class="btn btn-secondary" type="button" (click)="searchForm.get('searchText')?.setValue('');" autocomplete="off" i18n="Search Clear Button">Clear</button>
|
||||
@@ -27,7 +27,7 @@
|
||||
<td>{{ asset.ticker }}</td>
|
||||
<td class="d-none d-md-block">{{ asset.entity && asset.entity.domain }}</td>
|
||||
<td><a [routerLink]="['/asset/' | relativeUrl, asset.asset_id]">{{ asset.asset_id | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.asset_id"></app-clipboard></td>
|
||||
</tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td class="d-none d-md-block"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -68,4 +68,4 @@
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@@ -3,4 +3,11 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.title-asset {
|
||||
h1 {
|
||||
line-height: 1;
|
||||
margin: 0px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<div class="title-block">
|
||||
<h1><ng-template [ngIf]="blockHeight" i18n="block.block">Block <a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
|
||||
<h1><ng-template [ngIf]="blockHeight" i18n="shared.block-title">Block <ng-container *ngTemplateOutlet="blockTemplateContent"></ng-container></ng-template></h1>
|
||||
</div>
|
||||
|
||||
<ng-template #blockTemplateContent><a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<ng-template [ngIf]="!isLoading && !error">
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<div class="title-block">
|
||||
<ng-template [ngIf]="!isLoading && !error">
|
||||
|
||||
<div>
|
||||
<div class="title-block">
|
||||
<div class="title">
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
</div>
|
||||
|
||||
<div class="tx-link">
|
||||
<span class="tx-link float-left">
|
||||
<a [routerLink]="['/bisq-tx' | relativeUrl, bisqTx.id]">
|
||||
<span class="d-inline d-lg-none">{{ bisqTx.id | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ bisqTx.id }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="bisqTx.id"></app-clipboard>
|
||||
</div>
|
||||
</span>
|
||||
<span class="grow"></span>
|
||||
<div class="container-buttons">
|
||||
<button *ngIf="(latestBlock$ | async) as latestBlock" type="button" class="btn btn-sm btn-success float-right">
|
||||
<ng-container *ngTemplateOutlet="latestBlock.height - bisqTx.blockHeight + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - bisqTx.blockHeight + 1}"></ng-container>
|
||||
@@ -22,6 +21,8 @@
|
||||
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="box transaction-container">
|
||||
@@ -84,25 +85,33 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
|
||||
<div class="title">
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
</div>
|
||||
|
||||
<app-bisq-transaction-details [tx]="bisqTx"></app-bisq-transaction-details>
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
<div class="title">
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
</div>
|
||||
|
||||
<app-bisq-transfers [tx]="bisqTx"></app-bisq-transfers>
|
||||
|
||||
<br>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf="isLoading && !error">
|
||||
<ng-template [ngIf]="isLoading && !error">
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="title-block">
|
||||
<div class="title">
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
@@ -112,6 +121,14 @@
|
||||
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -122,6 +139,10 @@
|
||||
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -130,7 +151,10 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
<div class="title">
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
@@ -151,18 +175,30 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
|
||||
<div class="title">
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="row">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -178,4 +214,4 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,12 @@ 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';
|
||||
import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component';
|
||||
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -30,6 +31,10 @@ const routes: Routes = [
|
||||
path: 'market/:pair',
|
||||
component: BisqMarketComponent,
|
||||
},
|
||||
{
|
||||
path: 'tx/push',
|
||||
component: PushTransactionComponent,
|
||||
},
|
||||
{
|
||||
path: 'tx/:id',
|
||||
component: BisqTransactionComponent
|
||||
@@ -55,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',
|
||||
|
||||
@@ -16,6 +16,7 @@ export function calcSegwitFeeGains(tx: Transaction) {
|
||||
const isP2sh = vin.prevout.scriptpubkey_type === 'p2sh';
|
||||
const isP2wsh = vin.prevout.scriptpubkey_type === 'v0_p2wsh';
|
||||
const isP2wpkh = vin.prevout.scriptpubkey_type === 'v0_p2wpkh';
|
||||
const isP2tr = vin.prevout.scriptpubkey_type === 'v1_p2tr';
|
||||
|
||||
const op = vin.scriptsig ? vin.scriptsig_asm.split(' ')[0] : null;
|
||||
const isP2sh2Wpkh = isP2sh && !!vin.witness && op === 'OP_PUSHBYTES_22';
|
||||
@@ -25,6 +26,7 @@ export function calcSegwitFeeGains(tx: Transaction) {
|
||||
// Native Segwit - P2WPKH/P2WSH (Bech32)
|
||||
case isP2wpkh:
|
||||
case isP2wsh:
|
||||
case isP2tr:
|
||||
// maximal gains: the scriptSig is moved entirely to the witness part
|
||||
realizedGains += witnessSize(vin) * 3;
|
||||
// XXX P2WSH output creation is more expensive, should we take this into consideration?
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
<div class="enterprise-sponsor">
|
||||
<h3 i18n="about.sponsors.enterprise.withRocket">Enterprise Sponsors 🚀</h3>
|
||||
<div class="wrapper">
|
||||
<a href="https://squarecrypto.org/" target="_blank" title="Square Crypto">
|
||||
<img class="image" src="/resources/profile/sqcrypto.svg" />
|
||||
<span>Square</span>
|
||||
<a href="https://spiral.xyz/" target="_blank" title="Spiral">
|
||||
<img class="image" src="/resources/profile/spiral.svg" />
|
||||
<span>Spiral</span>
|
||||
</a>
|
||||
<a href="https://gemini.com/" target="_blank" title="Gemini">
|
||||
<img class="image" src="/resources/profile/gemini.svg" />
|
||||
@@ -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>
|
||||
|
||||
@@ -159,20 +163,32 @@
|
||||
</a>
|
||||
</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="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_staff">Project Staff</h3>
|
||||
<div class="wrapper">
|
||||
<ng-template ngFor let-contributor [ngForOf]="contributors.core">
|
||||
<a [href]="'https://github.com/' + contributor.name" target="_blank" [title]="contributor.name">
|
||||
<img class="image" [src]="'/api/v1/contributors/images/' + contributor.id" />
|
||||
<span>{{ contributor.name }}</span>
|
||||
</a>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="maintainers">
|
||||
<h3 i18n="about.maintainers">Project Maintainers</h3>
|
||||
|
||||
@@ -6,6 +6,7 @@ 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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-about',
|
||||
@@ -16,7 +17,7 @@ import { Router } from '@angular/router';
|
||||
export class AboutComponent implements OnInit {
|
||||
backendInfo$: Observable<IBackendInfo>;
|
||||
sponsors$: Observable<any>;
|
||||
contributors$: Observable<any>;
|
||||
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 +38,14 @@ export class AboutComponent implements OnInit {
|
||||
this.websocketService.want(['blocks']);
|
||||
|
||||
this.sponsors$ = this.apiService.getDonation$();
|
||||
this.contributors$ = this.apiService.getContributor$();
|
||||
this.allContributors$ = this.apiService.getContributor$().pipe(
|
||||
map((contributors) => {
|
||||
return {
|
||||
regular: contributors.filter((user) => !user.core_constributor),
|
||||
core: contributors.filter((user) => user.core_constributor),
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sponsor() {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<div class="container-xl">
|
||||
<h1 i18n="shared.address">Address</h1>
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/address/' | relativeUrl, addressString]" >
|
||||
<span class="d-inline d-lg-none">{{ addressString | shortenString : 18 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressString }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="addressString"></app-clipboard>
|
||||
<div class="title-address">
|
||||
<h1 i18n="shared.address">Address</h1>
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/address/' | relativeUrl, addressString]" >
|
||||
<span class="d-inline d-lg-none">{{ addressString | shortenString : 18 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressString }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="addressString"></app-clipboard>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -20,12 +21,15 @@
|
||||
<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>
|
||||
<td i18n="address.total-received">Total received</td>
|
||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved" [noFiat]="true"></app-amount></td>
|
||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="received" [noFiat]="true"></app-amount></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="address.total-sent">Total sent</td>
|
||||
@@ -34,7 +38,7 @@
|
||||
</ng-template>
|
||||
<tr>
|
||||
<td i18n="address.balance">Balance</td>
|
||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved - sent" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="receieved - sent"></app-fiat></span></td>
|
||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="received - sent" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="received - sent"></app-fiat></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -50,12 +54,13 @@
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<h2>
|
||||
<ng-template [ngIf]="!transactions?.length"> </ng-template>
|
||||
<ng-template i18n="X of X Address Transaction" [ngIf]="transactions?.length === 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transaction</ng-template>
|
||||
<ng-template i18n="X of X Address Transactions (Plural)" [ngIf]="transactions?.length > 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transactions</ng-template>
|
||||
</h2>
|
||||
<div class="title-tx">
|
||||
<h2>
|
||||
<ng-template [ngIf]="!transactions?.length"> </ng-template>
|
||||
<ng-template i18n="X of X Address Transaction" [ngIf]="transactions?.length === 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transaction</ng-template>
|
||||
<ng-template i18n="X of X Address Transactions (Plural)" [ngIf]="transactions?.length > 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transactions</ng-template>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
|
||||
|
||||
@@ -85,7 +90,7 @@
|
||||
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="retryLoadmore">
|
||||
<ng-template [ngIf]="retryLoadMore">
|
||||
<br>
|
||||
<button type="button" class="btn btn-outline-info btn-sm" (click)="loadMore()"><fa-icon [icon]="['fas', 'redo-alt']" [fixedWidth]="true"></fa-icon></button>
|
||||
</ng-template>
|
||||
@@ -114,7 +119,7 @@
|
||||
</div>
|
||||
<div class="w-100 d-block d-md-none"></div>
|
||||
<div class="col">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,10 +127,11 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="error">
|
||||
<br>
|
||||
<div class="text-center">
|
||||
<span i18n="address.error.loading-address-data">Error loading address data.</span>
|
||||
<br>
|
||||
<ng-template #displayServerError><i>{{ error.error }}</i></ng-template>
|
||||
<ng-template #displayServerError><i class="small">({{ error.error }})</i></ng-template>
|
||||
<ng-template [ngIf]="error.status === 413 || error.status === 405" [ngIfElse]="displayServerError">
|
||||
<ng-container i18n="Electrum server limit exceeded error">
|
||||
<i>The number of transactions on this address exceeds the Electrum server limit</i>
|
||||
@@ -136,6 +142,8 @@
|
||||
<a href="https://mempool.space/address/{{ addressString }}" target="_blank">https://mempool.space/address/{{ addressString }}</a>
|
||||
<br>
|
||||
<a href="http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/address/{{ addressString }}" target="_blank">http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/address/{{ addressString }}</a>
|
||||
<br><br>
|
||||
<i class="small">({{ error.error }})</i>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -35,17 +35,22 @@
|
||||
h1 {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
margin-right: 10px;
|
||||
font-size: 1.9rem;
|
||||
@media (min-width: 576px) {
|
||||
font-size: 2rem;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.address-link {
|
||||
line-height: 56px;
|
||||
line-height: 56px;
|
||||
margin-left: 0px;
|
||||
top: -2px;
|
||||
position: relative;
|
||||
position: relative;
|
||||
@media (min-width: 768px) {
|
||||
line-height: 69px;
|
||||
}
|
||||
@@ -69,10 +74,20 @@ h1 {
|
||||
|
||||
.tx-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
top: 14px;
|
||||
height: 100%;
|
||||
top: 9px;
|
||||
position: relative;
|
||||
@media (min-width: 576px) {
|
||||
top: 11px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
top: 20px;
|
||||
top: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-tx {
|
||||
h2 {
|
||||
line-height: 1;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
isLoadingAddress = true;
|
||||
transactions: Transaction[];
|
||||
isLoadingTransactions = true;
|
||||
retryLoadmore = false;
|
||||
retryLoadMore = false;
|
||||
error: any;
|
||||
mainSubscription: Subscription;
|
||||
addressLoadingStatus$: Observable<number>;
|
||||
@@ -33,7 +33,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
totalConfirmedTxCount = 0;
|
||||
loadedConfirmedTxCount = 0;
|
||||
txCount = 0;
|
||||
receieved = 0;
|
||||
received = 0;
|
||||
sent = 0;
|
||||
|
||||
private tempTransactions: Transaction[];
|
||||
@@ -183,7 +183,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
transaction.vout.forEach((vout) => {
|
||||
if (vout.scriptpubkey_address === this.address.address) {
|
||||
this.receieved += vout.value;
|
||||
this.received += vout.value;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -206,7 +206,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
this.isLoadingTransactions = true;
|
||||
this.retryLoadmore = false;
|
||||
this.retryLoadMore = false;
|
||||
this.electrsApiService.getAddressTransactionsFromHash$(this.address.address, this.lastTransactionTxId)
|
||||
.subscribe((transactions: Transaction[]) => {
|
||||
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
||||
@@ -216,12 +216,12 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
},
|
||||
(error) => {
|
||||
this.isLoadingTransactions = false;
|
||||
this.retryLoadmore = true;
|
||||
this.retryLoadMore = true;
|
||||
});
|
||||
}
|
||||
|
||||
updateChainStats() {
|
||||
this.receieved = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum;
|
||||
this.received = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum;
|
||||
this.sent = this.address.chain_stats.spent_txo_sum + this.address.mempool_stats.spent_txo_sum;
|
||||
this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count;
|
||||
this.totalConfirmedTxCount = this.address.chain_stats.tx_count;
|
||||
|
||||
@@ -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="api-docs.tab.bsq|API Docs tab for BSQ">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,11 +1,14 @@
|
||||
<div class="container-xl">
|
||||
<h1 style="float: left;" i18n="asset|Liquid Asset page title">Asset</h1>
|
||||
<a [routerLink]="['/asset/' | relativeUrl, assetString]" style="line-height: 56px; margin-left: 10px;">
|
||||
<span class="d-inline d-lg-none">{{ assetString | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ assetString }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="assetString"></app-clipboard>
|
||||
<br>
|
||||
<div class="title-asset">
|
||||
<h1 i18n="asset|Liquid Asset page title">Asset</h1>
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/asset/' | relativeUrl, assetString]">
|
||||
<span class="d-inline d-lg-none">{{ assetString | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ assetString }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="assetString"></app-clipboard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -72,12 +75,14 @@
|
||||
|
||||
<br>
|
||||
|
||||
<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 #defaultAsset i18n="Default asset transactions title">Issuance and Burn Transactions</ng-template>
|
||||
</h2>
|
||||
|
||||
<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 #defaultAsset i18n="Default asset transactions title">Issuance and Burn Transactions</ng-template>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
|
||||
|
||||
<div class="text-center">
|
||||
|
||||
@@ -20,4 +20,33 @@
|
||||
margin-top: 20px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
margin-right: 15px;
|
||||
@media (min-width: 576px) {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.tx-link {
|
||||
display: block;
|
||||
height: 100%;
|
||||
top: 9px;
|
||||
position: relative;
|
||||
@media (min-width: 576px) {
|
||||
top: 11px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
top: 17px;
|
||||
}
|
||||
}
|
||||
.title-tx {
|
||||
h2 {
|
||||
line-height: 1;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,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>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
<div class="title-block" id="block">
|
||||
<h1>
|
||||
<ng-template [ngIf]="blockHeight === 0" i18n="block.genesis">Genesis
|
||||
<div class="next-previous-blocks">
|
||||
<ng-template [ngIf]="blockHeight === 0"><ng-container i18n="@@2303359202781425764">Genesis</ng-container>
|
||||
<span class="next-previous-blocks">
|
||||
<a *ngIf="showNextBlocklink" [routerLink]="['/block/' | relativeUrl, nextBlockHeight]" (click)="navigateToNextBlock()" i18n-ngbTooltip="Next Block" ngbTooltip="Next Block" placement="bottom">
|
||||
<fa-icon [icon]="['fas', 'angle-left']" [fixedWidth]="true"></fa-icon>
|
||||
</a>
|
||||
@@ -11,10 +11,11 @@
|
||||
<span placement="bottom" class="disable">
|
||||
<fa-icon [icon]="['fas', 'angle-right']" [fixedWidth]="true"></fa-icon>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="blockHeight" i18n="block.block"> Block
|
||||
<div class="next-previous-blocks">
|
||||
<ng-template [ngIf]="blockHeight" i18n="shared.block-title">Block <ng-container *ngTemplateOutlet="blockTemplateContent"></ng-container></ng-template>
|
||||
<ng-template #blockTemplateContent>
|
||||
<span class="next-previous-blocks">
|
||||
<a *ngIf="showNextBlocklink" [routerLink]="['/block/' | relativeUrl, nextBlockHeight]" (click)="navigateToNextBlock()" i18n-ngbTooltip="Next Block" ngbTooltip="Next Block" placement="bottom">
|
||||
<fa-icon [icon]="['fas', 'angle-left']" [fixedWidth]="true"></fa-icon>
|
||||
</a>
|
||||
@@ -28,7 +29,7 @@
|
||||
<span *ngIf="!showPreviousBlocklink" placement="bottom" class="disable">
|
||||
<fa-icon [icon]="['fas', 'angle-right']" [fixedWidth]="true"></fa-icon>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</ng-template>
|
||||
</h1>
|
||||
|
||||
@@ -162,7 +163,7 @@
|
||||
</div>
|
||||
|
||||
<div #blockTxTitle id="block-tx-title" class="block-tx-title">
|
||||
<h2 class="float-left">
|
||||
<h2>
|
||||
<ng-container *ngTemplateOutlet="block.tx_count === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.tx_count | number}"></ng-container>
|
||||
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
|
||||
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
|
||||
@@ -175,14 +176,14 @@
|
||||
<app-transactions-list [transactions]="transactions"></app-transactions-list>
|
||||
|
||||
<ng-template [ngIf]="isLoadingTransactions">
|
||||
<div class="text-center mb-4 mt-3">
|
||||
<div class="text-center mb-4" class="tx-skeleton">
|
||||
|
||||
<div class="header-bg box" style="padding: 10px; margin-bottom: 10px;">
|
||||
<div class="header-bg box">
|
||||
<span class="skeleton-loader"></span>
|
||||
</div>
|
||||
|
||||
<div class="header-bg box">
|
||||
<div class="row" style="height: 107px;">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<span class="skeleton-loader"></span>
|
||||
</div>
|
||||
@@ -199,7 +200,7 @@
|
||||
<div class="progress-bar progress-darklight" role="progressbar" [ngStyle]="{'width': txsLoadingStatus + '%' }"></div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
<ngb-pagination class="pagination-container float-right" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page, blockTxTitle)" [maxSize]="paginationMaxSize" [boundaryLinks]="true" [ellipses]="false"></ngb-pagination>
|
||||
@@ -207,7 +208,7 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf]="isLoadingBlock && !error">
|
||||
|
||||
|
||||
<div class="box">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
|
||||
.fiat {
|
||||
display: block;
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
@media (min-width: 768px) {
|
||||
font-size: 14px;
|
||||
@@ -40,10 +40,7 @@
|
||||
h1 {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
@media (min-width: 576px) {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
line-height: 1;
|
||||
a {
|
||||
&:hover, &:focus{
|
||||
text-decoration: none;;
|
||||
@@ -87,32 +84,23 @@ h1 {
|
||||
}
|
||||
|
||||
.block-tx-title {
|
||||
padding-top: 10px;
|
||||
display: block;
|
||||
text-align: right;
|
||||
margin-top: -30px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
margin-top: -15px;
|
||||
position: relative;
|
||||
@media (min-width: 550px) {
|
||||
margin-top: 0px;
|
||||
padding-top: 10px;
|
||||
margin-top: 1rem;
|
||||
flex-direction: row;
|
||||
}
|
||||
h2 {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
line-height: 1.6;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
margin-bottom: -15px;
|
||||
padding-right: 10px;
|
||||
padding-top: 15px;
|
||||
position: relative;
|
||||
top: -22px;
|
||||
width: auto;
|
||||
padding-bottom: 10px;
|
||||
@media (min-width: 550px) {
|
||||
padding-top: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
padding-top: 5px;
|
||||
line-height: 1;
|
||||
padding-bottom: 0px;
|
||||
align-self: end;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,22 +110,41 @@ h1 {
|
||||
}
|
||||
|
||||
.next-previous-blocks {
|
||||
font-size: 36px;
|
||||
font-size: 28px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
@media (min-width: 768px) {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #1ad8f4;
|
||||
&:hover, &:focus {
|
||||
color: #09a3ba;
|
||||
display: inline-block;
|
||||
// transform: scale(1.2);
|
||||
// transition: 150ms all ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disable {
|
||||
font-size: 36px;
|
||||
font-size: 28px;
|
||||
color: #393e5c73;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
font-size: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.tx-skeleton {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
.header-bg {
|
||||
&:first-child {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
.row {
|
||||
height: 107px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="blocks-container blockchain-blocks-container" *ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate">
|
||||
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
|
||||
<div class="text-center bitcoin-block mined-block blockchain-blocks-{{ i }}" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]">
|
||||
<div class="text-center bitcoin-block mined-block blockchain-blocks-{{ i }}" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]" [class.blink-bg]="(specialBlocks[block.height] !== undefined)">
|
||||
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink"> </a>
|
||||
<div class="block-height">
|
||||
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a>
|
||||
@@ -25,7 +25,7 @@
|
||||
<div [hidden]="!arrowVisible" id="arrow-up" [style.transition]="transition" [ngStyle]="{'left': arrowLeftPx + 'px' }"></div>
|
||||
</div>
|
||||
|
||||
<ng-template #loadingBlocksTemplate >
|
||||
<ng-template #loadingBlocksTemplate>
|
||||
<div class="blocks-container">
|
||||
<div class="flashing">
|
||||
<div *ngFor="let block of emptyBlocks; let i = index; trackBy: trackByBlocksFn" >
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
|
||||
.flashing {
|
||||
animation: opacityPulse 2s ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
animation-iteration-count: infinite;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@
|
||||
0% {opacity: 0.7;}
|
||||
50% {opacity: 1.0;}
|
||||
100% {opacity: 0.7;}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { Block } from 'src/app/interfaces/electrs.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { specialBlocks } from 'src/app/app.constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-blockchain-blocks',
|
||||
@@ -11,7 +12,7 @@ import { Router } from '@angular/router';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
specialBlocks = specialBlocks;
|
||||
network = '';
|
||||
blocks: Block[] = [];
|
||||
emptyBlocks: Block[] = this.mountEmptyBlocks();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.btn-link {
|
||||
padding: 0.25rem 0 0.1rem 0.5rem;
|
||||
}
|
||||
|
||||
880
frontend/src/app/components/docs/api-docs.component.html
Normal file
880
frontend/src/app/components/docs/api-docs.component.html
Normal file
@@ -0,0 +1,880 @@
|
||||
<ng-container *ngIf="{ val: network$ | async } as network">
|
||||
<div class="container-xl text-left">
|
||||
|
||||
<div id="restAPI" *ngIf="restTabActivated">
|
||||
|
||||
<p>Reference for the {{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API service</ng-container>.</p>
|
||||
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq' && network.val !== 'liquid'">
|
||||
<h4 i18n="api-docs.tab.general|API Docs tab for General">General</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark">
|
||||
|
||||
<ngb-panel id="difficultyAdjustment" *ngIf="network.val !== 'liquid'">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val === 'bisq'">
|
||||
<h4 i18n="Bisq All Markets">Markets</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="bisqMarketsCurrencies">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val === 'bisq'">
|
||||
<h4 ngbNavLink i18n="api-docs.tab.bsq|API Docs tab for BSQ">General</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="bisqStats">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category">
|
||||
<h4 i18n="api-docs.tab.addresses|API Docs tab for Addresses">Addresses</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="address">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val === 'liquid'">
|
||||
<h4 i18n="api-docs.tab.assets|API Docs tab for Assets">Assets</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="assets">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category">
|
||||
<h4 i18n="api-docs.tab.blocks|API Docs tab for Blocks">Blocks</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="block">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq'">
|
||||
<h4 i18n="api-docs.tab.fees|API Docs tab for Fees">Fees</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark">
|
||||
|
||||
<ngb-panel id="feeMempoolBlocks">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category" *ngIf="network.val !== 'bisq'">
|
||||
<h4 i18n="api-docs.tab.mempool|API Docs tab for Mempool">Mempool</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel id="mempool">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="api-category">
|
||||
<h4 i18n="api-docs.tab.transactions|API Docs tab for Transactions">Transactions</h4>
|
||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||
|
||||
<ngb-panel *ngIf="network.val !== 'bisq'" id="cpfp">
|
||||
<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>
|
||||
</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>
|
||||
|
||||
<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>
|
||||
@@ -29,7 +29,7 @@ li.nav-item {
|
||||
}
|
||||
}
|
||||
|
||||
.code-tab {
|
||||
.code-tab {
|
||||
width: auto;
|
||||
margin: 20px auto 10px;
|
||||
li.nav-item {
|
||||
@@ -67,10 +67,15 @@ li.nav-item {
|
||||
height: 1px;
|
||||
background: #333;
|
||||
}
|
||||
.websocket {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.difficulty {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
#restAPI .api-category {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.api-category h4 {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Env, StateService } from 'src/app/services/state.service';
|
||||
import { Observable, merge, of } from 'rxjs';
|
||||
import { SeoService } from 'src/app/services/seo.service';
|
||||
@@ -16,6 +16,7 @@ export class ApiDocsComponent implements OnInit {
|
||||
env: Env;
|
||||
code: any;
|
||||
baseNetworkUrl = '';
|
||||
@Input() restTabActivated: Boolean;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
@@ -3416,8 +3417,8 @@ export class ApiDocsComponent implements OnInit {
|
||||
|
||||
ws.addEventListener('message', function incoming({data}) {
|
||||
const res = JSON.parse(data.toString());
|
||||
if (res.blocks) {
|
||||
document.getElementById("result-blocks").textContent = JSON.stringify(res.blocks, undefined, 2);
|
||||
if (res.block) {
|
||||
document.getElementById("result-blocks").textContent = JSON.stringify(res.block, undefined, 2);
|
||||
}
|
||||
if (res.mempoolInfo) {
|
||||
document.getElementById("result-mempool-info").textContent = JSON.stringify(res.mempoolInfo, undefined, 2);
|
||||
@@ -3425,8 +3426,8 @@ export class ApiDocsComponent implements OnInit {
|
||||
if (res.transactions) {
|
||||
document.getElementById("result-transactions").textContent = JSON.stringify(res.transactions, undefined, 2);
|
||||
}
|
||||
if (res.mempoolBlocks) {
|
||||
document.getElementById("result-mempool-blocks").textContent = JSON.stringify(res.mempoolBlocks, undefined, 2);
|
||||
if (res["mempool-blocks"]) {
|
||||
document.getElementById("result-mempool-blocks").textContent = JSON.stringify(res["mempool-blocks"], undefined, 2);
|
||||
}
|
||||
});
|
||||
`,
|
||||
@@ -3439,8 +3440,8 @@ export class ApiDocsComponent implements OnInit {
|
||||
|
||||
ws.on("message", function incoming(data) {
|
||||
const res = JSON.parse(data.toString());
|
||||
if (res.blocks) {
|
||||
console.log(res.blocks);
|
||||
if (res.block) {
|
||||
console.log(res.block);
|
||||
}
|
||||
if (res.mempoolInfo) {
|
||||
console.log(res.mempoolInfo);
|
||||
@@ -3448,8 +3449,8 @@ export class ApiDocsComponent implements OnInit {
|
||||
if (res.transactions) {
|
||||
console.log(res.transactions);
|
||||
}
|
||||
if (res.mempoolBlocks) {
|
||||
console.log(res.mempoolBlocks);
|
||||
if (res["mempool-blocks"]) {
|
||||
console.log(res["mempool-blocks"]);
|
||||
}
|
||||
});
|
||||
`,
|
||||
@@ -174,10 +174,10 @@ init();`;
|
||||
|
||||
let resultHtml = '<pre id="result"></pre>';
|
||||
if (this.method === 'websocket') {
|
||||
resultHtml = `<pre id="result-blocks"></pre>
|
||||
<pre id="result-mempool-info"></pre>
|
||||
<pre id="result-transactions"></pre>
|
||||
<pre id="result-mempool-blocks"></pre>`;
|
||||
resultHtml = `<h2>Blocks</h2><pre id="result-blocks">Waiting for data</pre><br>
|
||||
<h2>Mempool Info</h2><pre id="result-mempool-info">Waiting for data</pre><br>
|
||||
<h2>Transactions</h2><pre id="result-transactions">Waiting for data</pre><br>
|
||||
<h2>Mempool Blocks</h2><pre id="result-mempool-blocks">Waiting for data</pre><br>`;
|
||||
}
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
31
frontend/src/app/components/docs/docs.component.html
Normal file
31
frontend/src/app/components/docs/docs.component.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<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>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
4
frontend/src/app/components/docs/docs.component.scss
Normal file
4
frontend/src/app/components/docs/docs.component.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
#main-tab-content {
|
||||
text-align: left;
|
||||
padding-top: 10px;
|
||||
}
|
||||
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" ) ) );
|
||||
}
|
||||
}
|
||||
@@ -1 +1,4 @@
|
||||
<div class="echarts" echarts [initOpts]="mempoolStatsChartInitOption" [options]="mempoolStatsChartOption"></div>
|
||||
<div class="echarts" echarts [initOpts]="mempoolStatsChartInitOption" [options]="mempoolStatsChartOption" (chartRendered)="rendered()"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
@@ -1,12 +1,20 @@
|
||||
import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { formatDate } from '@angular/common';
|
||||
import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
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',
|
||||
templateUrl: './incoming-transactions-graph.component.html',
|
||||
styles: [`
|
||||
.loadingGraphs {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: calc(50% - 16px);
|
||||
z-index: 100;
|
||||
}
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
@@ -17,7 +25,9 @@ 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 = {};
|
||||
mempoolStatsChartInitOption = {
|
||||
renderer: 'svg'
|
||||
@@ -29,13 +39,23 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
private storageService: StorageService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.isLoading = true;
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.windowPreference = this.storageService.getValue('graphWindowPreference');
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference');
|
||||
this.mountChart();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.mountChart();
|
||||
rendered() {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
mountChart(): void {
|
||||
@@ -54,10 +74,12 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
maxSpan: 100,
|
||||
minSpan: 10,
|
||||
}, {
|
||||
showDetail: false,
|
||||
show: (this.template === 'advanced') ? true : false,
|
||||
type: 'slider',
|
||||
brushSelect: false,
|
||||
realtime: true,
|
||||
bottom: 0,
|
||||
selectedDataBackground: {
|
||||
lineStyle: {
|
||||
color: '#fff',
|
||||
@@ -66,7 +88,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||
areaStyle: {
|
||||
opacity: 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
}],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
@@ -83,29 +105,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: {
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
<div class="echarts" echarts [initOpts]="pegsChartInitOption" [options]="pegsChartOptions"></div>
|
||||
<div class="echarts" echarts [initOpts]="pegsChartInitOption" [options]="pegsChartOptions" (chartRendered)="rendered()"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
@@ -1,14 +1,22 @@
|
||||
import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core';
|
||||
import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { formatDate, formatNumber } from '@angular/common';
|
||||
import { EChartsOption } from 'echarts';
|
||||
|
||||
@Component({
|
||||
selector: 'app-lbtc-pegs-graph',
|
||||
styles: [`::ng-deep .tx-wrapper-tooltip-chart { width: 135px; }`],
|
||||
styles: [`
|
||||
::ng-deep .tx-wrapper-tooltip-chart { width: 135px; }
|
||||
.loadingGraphs {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: calc(50% - 16px);
|
||||
z-index: 100;
|
||||
}
|
||||
`],
|
||||
templateUrl: './lbtc-pegs-graph.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class LbtcPegsGraphComponent implements OnChanges {
|
||||
export class LbtcPegsGraphComponent implements OnInit, OnChanges {
|
||||
@Input() data: any;
|
||||
pegsChartOptions: EChartsOption;
|
||||
|
||||
@@ -17,6 +25,7 @@ export class LbtcPegsGraphComponent implements OnChanges {
|
||||
top: number | string = '20';
|
||||
left: number | string = '50';
|
||||
template: ('widget' | 'advanced') = 'widget';
|
||||
isLoading = true;
|
||||
|
||||
pegsChartOption: EChartsOption = {};
|
||||
pegsChartInitOption = {
|
||||
@@ -27,6 +36,10 @@ export class LbtcPegsGraphComponent implements OnChanges {
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.isLoading = true;
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (!this.data) {
|
||||
return;
|
||||
@@ -34,6 +47,13 @@ export class LbtcPegsGraphComponent implements OnChanges {
|
||||
this.pegsChartOptions = this.createChartOptions(this.data.series, this.data.labels);
|
||||
}
|
||||
|
||||
rendered() {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
createChartOptions(series: number[], labels: string[]): EChartsOption {
|
||||
return {
|
||||
grid: {
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<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>
|
||||
</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 +59,4 @@
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@@ -57,8 +57,8 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
@@ -72,6 +72,14 @@ li.nav-item {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
.dropdown {
|
||||
.dropdown-toggle {
|
||||
width: 62px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.navbar-brand {
|
||||
width: 140px;
|
||||
@@ -132,4 +140,4 @@ nav {
|
||||
}
|
||||
.navbar-dark .navbar-nav .nav-link {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="container-xl" *ngIf="mempoolBlock$ | async as mempoolBlock">
|
||||
|
||||
<div class="title-block">
|
||||
<h1 class="float-left">{{ ordinal$ | async }}</h1>
|
||||
<h1>{{ ordinal$ | async }}</h1>
|
||||
<button [routerLink]="['/' | relativeUrl]" class="btn btn-sm float-right">✕</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,13 +4,6 @@
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
color: #FFF;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 3px;
|
||||
border-top: 3px solid #FFF;
|
||||
}
|
||||
|
||||
.fiat {
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
@@ -31,9 +24,9 @@
|
||||
h1 {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
line-height: 1;
|
||||
@media (min-width: 576px) {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
|
||||
<div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
|
||||
<div class="flashing">
|
||||
<div class="flashing">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
|
||||
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
|
||||
<div class="block-body">
|
||||
<div class="fees">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
.flashing {
|
||||
animation: opacityPulse 2s ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
animation-iteration-count: infinite;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
|
||||
.bitcoin-block::after {
|
||||
content: '';
|
||||
content: '';
|
||||
width: 125px;
|
||||
height: 24px;
|
||||
position:absolute;
|
||||
@@ -68,7 +68,7 @@
|
||||
left: -20px;
|
||||
background-color: #232838;
|
||||
transform:skew(40deg);
|
||||
transform-origin:top;
|
||||
transform-origin:top;
|
||||
}
|
||||
|
||||
.bitcoin-block::before {
|
||||
@@ -78,18 +78,18 @@
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: -20px;
|
||||
background-color: #191c27;
|
||||
|
||||
background-color: #191c27;
|
||||
|
||||
transform: skewY(50deg);
|
||||
transform-origin: top;
|
||||
transform-origin: top;
|
||||
}
|
||||
|
||||
.mempool-block.bitcoin-block::after {
|
||||
background-color: #403834;
|
||||
background-color: #403834;
|
||||
}
|
||||
|
||||
.mempool-block.bitcoin-block::before {
|
||||
background-color: #2d2825;
|
||||
background-color: #2d2825;
|
||||
}
|
||||
|
||||
.black-background {
|
||||
@@ -102,8 +102,8 @@
|
||||
position: relative;
|
||||
right: 75px;
|
||||
top: 140px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 35px solid transparent;
|
||||
border-right: 35px solid transparent;
|
||||
border-bottom: 35px solid #FFF;
|
||||
|
||||
@@ -3,8 +3,9 @@ import { Subscription, Observable, fromEvent, merge, of, combineLatest, timer }
|
||||
import { MempoolBlock } from 'src/app/interfaces/websocket.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { take, map, switchMap, share } from 'rxjs/operators';
|
||||
import { take, map, switchMap } from 'rxjs/operators';
|
||||
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||
import { specialBlocks } from 'src/app/app.constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-blocks',
|
||||
@@ -13,12 +14,13 @@ import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
specialBlocks = specialBlocks;
|
||||
mempoolBlocks: MempoolBlock[] = [];
|
||||
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
||||
mempoolBlocks$: Observable<MempoolBlock[]>;
|
||||
timeAvg$: Observable<number>;
|
||||
loadingBlocks$: Observable<boolean>;
|
||||
blocksSubscription: Subscription;
|
||||
|
||||
mempoolBlocksFull: MempoolBlock[] = [];
|
||||
mempoolBlockStyles = [];
|
||||
@@ -74,26 +76,36 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
fromEvent(window, 'resize')
|
||||
)
|
||||
.pipe(
|
||||
switchMap(() => this.stateService.mempoolBlocks$),
|
||||
map((blocks) => {
|
||||
if (!blocks.length) {
|
||||
return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }];
|
||||
}
|
||||
return blocks;
|
||||
}),
|
||||
map((blocks) => {
|
||||
blocks.forEach((block, i) => {
|
||||
block.index = this.blockIndex + i;
|
||||
});
|
||||
const stringifiedBlocks = JSON.stringify(blocks);
|
||||
this.mempoolBlocksFull = JSON.parse(stringifiedBlocks);
|
||||
this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(stringifiedBlocks));
|
||||
this.updateMempoolBlockStyles();
|
||||
this.calculateTransactionPosition();
|
||||
return this.mempoolBlocks;
|
||||
})
|
||||
);
|
||||
switchMap(() => combineLatest([
|
||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||
this.stateService.mempoolBlocks$
|
||||
.pipe(
|
||||
map((mempoolBlocks) => {
|
||||
if (!mempoolBlocks.length) {
|
||||
return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }];
|
||||
}
|
||||
return mempoolBlocks;
|
||||
}),
|
||||
)
|
||||
])),
|
||||
map(([lastBlock, mempoolBlocks]) => {
|
||||
mempoolBlocks.forEach((block, i) => {
|
||||
block.index = this.blockIndex + i;
|
||||
block.height = lastBlock.height + i + 1;
|
||||
if (this.stateService.network === '') {
|
||||
block.blink = specialBlocks[block.height] ? true : false;
|
||||
}
|
||||
});
|
||||
|
||||
const stringifiedBlocks = JSON.stringify(mempoolBlocks);
|
||||
this.mempoolBlocksFull = JSON.parse(stringifiedBlocks);
|
||||
this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(stringifiedBlocks));
|
||||
|
||||
this.updateMempoolBlockStyles();
|
||||
this.calculateTransactionPosition();
|
||||
return this.mempoolBlocks;
|
||||
})
|
||||
);
|
||||
|
||||
this.timeAvg$ = timer(0, 1000)
|
||||
.pipe(
|
||||
@@ -118,7 +130,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
timeAvgMins += Math.abs(timeAvgDiff);
|
||||
}
|
||||
|
||||
|
||||
return timeAvgMins * 60 * 1000;
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
<div echarts class="echarts" (chartInit)="onChartReady($event)" [initOpts]="mempoolVsizeFeesInitOptions" [options]="mempoolVsizeFeesOptions"></div>
|
||||
<div echarts class="echarts" (chartInit)="onChartReady($event)" (chartRendered)="rendered()" [initOpts]="mempoolVsizeFeesInitOptions" [options]="mempoolVsizeFeesOptions"></div>
|
||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
@@ -1,17 +1,24 @@
|
||||
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',
|
||||
templateUrl: './mempool-graph.component.html',
|
||||
styles: [`
|
||||
.loadingGraphs {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: calc(50% - 16px);
|
||||
z-index: 100;
|
||||
}
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
@@ -24,7 +31,9 @@ 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;
|
||||
mempoolVsizeFeesOptions: EChartsOption;
|
||||
mempoolVsizeFeesInitOptions = {
|
||||
@@ -45,16 +54,26 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.isLoading = true;
|
||||
this.inverted = this.storageService.getValue('inverted-graph') === 'true';
|
||||
this.mountFeeChart();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.windowPreference = this.storageService.getValue('graphWindowPreference');
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference');
|
||||
this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([]));
|
||||
this.mountFeeChart();
|
||||
}
|
||||
|
||||
rendered() {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
onChartReady(myChart: any) {
|
||||
myChart.getZr().on('mousemove', (e: any) => {
|
||||
if (e.target !== undefined &&
|
||||
@@ -71,11 +90,6 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
const labels = mempoolStats.map(stats => stats.added);
|
||||
const finalArrayVByte = this.generateArray(mempoolStats);
|
||||
|
||||
// Only Liquid has lower than 1 sat/vb transactions
|
||||
if (this.stateService.network !== 'liquid') {
|
||||
finalArrayVByte.shift();
|
||||
}
|
||||
|
||||
return {
|
||||
labels: labels,
|
||||
series: finalArrayVByte
|
||||
@@ -83,13 +97,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
generateArray(mempoolStats: OptimizedMempoolStats[]) {
|
||||
const finalArray: number[][] = [];
|
||||
let feesArray: number[] = [];
|
||||
const limitFeesTemplate = this.template === 'advanced' ? 28 : 21;
|
||||
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);
|
||||
}
|
||||
@@ -99,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 = [];
|
||||
@@ -172,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;
|
||||
@@ -219,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">
|
||||
@@ -238,9 +253,12 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
</tr>`);
|
||||
});
|
||||
const classActive = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : '';
|
||||
const titleRange = $localize`Range`;
|
||||
const titleSize = $localize`:@@7faaaa08f56427999f3be41df1093ce4089bbd75:Size`;
|
||||
const titleSum = $localize`Sum`;
|
||||
return `<div class="fees-wrapper-tooltip-chart ${classActive}">
|
||||
<div class="title">
|
||||
${params[0].axisValue}
|
||||
${axisValueLabel}
|
||||
<span class="total-value">
|
||||
${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)}
|
||||
</span>
|
||||
@@ -248,9 +266,9 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Range</th>
|
||||
<th>Size</th>
|
||||
<th>Sum</th>
|
||||
<th>${titleRange}</th>
|
||||
<th>${titleSize}</th>
|
||||
<th>${titleSum}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -271,6 +289,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
maxSpan: 100,
|
||||
minSpan: 10,
|
||||
}, {
|
||||
showDetail: false,
|
||||
show: (this.template === 'advanced' && this.showZoom) ? true : false,
|
||||
type: 'slider',
|
||||
brushSelect: false,
|
||||
@@ -295,15 +314,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: {
|
||||
@@ -329,7 +355,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 {
|
||||
@@ -345,10 +371,10 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||
this.feeLimitIndex = i;
|
||||
}
|
||||
if (feeLevels[i] <= this.limitFee) {
|
||||
if (i === 0) {
|
||||
this.feeLevelsOrdered.push('0 - 1');
|
||||
if (this.stateService.network === 'liquid') {
|
||||
this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)} - ${(feeLevels[i + 1] / 10).toFixed(1)}`);
|
||||
} else {
|
||||
this.feeLevelsOrdered.push(`${feeLevels[i - 1]} - ${feeLevels[i]}`);
|
||||
this.feeLevelsOrdered.push(`${feeLevels[i]} - ${feeLevels[i + 1]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,40 +5,53 @@
|
||||
<br><br>
|
||||
|
||||
<h2>Privacy Policy</h2>
|
||||
<h6>Updated: August 2, 2021</h6>
|
||||
<h6>Updated: November 18, 2021</h6>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div class="text-left">
|
||||
|
||||
<p *ngIf="officialMempoolSpace">The <a href="https://mempool.space/">mempool.space</a> website, the <a href="https://liquid.network/">liquid.network</a> website, the <a href="https://bisq.markets/">bisq.markets</a> website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from <a href="https://wq.apnic.net/static/search.html?query=AS142052">AS142052</a>.</p>
|
||||
|
||||
<p *ngIf="!officialMempoolSpace">This website and its API service (collectively, the "Website") are operated by a member of the Bitcoin community ("We" or "Us"). Mempool Space K.K. in Japan ("Mempool") has no affiliation with the operator of this Website, and does not sponsor or endorse the information provided herein.</p>
|
||||
|
||||
<h5>By accessing this Website, you agree to the following Privacy Policy:</h5>
|
||||
|
||||
<br>
|
||||
|
||||
<h4>TRUSTED THIRD PARTIES ARE SECURITY HOLES</h4>
|
||||
|
||||
<ul>
|
||||
<li>We do not use any third-party analytics, trackers, or cookies.</li>
|
||||
<li>We do not share any private user data with third-parties.</li>
|
||||
</ul>
|
||||
<p>Out of respect for the Bitcoin community, this website does not use any third-party analytics, third-party trackers, or third-party cookies, and we do not share any private user data with third-parties. Additionally, to mitigate the risk of surveillance by malicious third-parties, we self-host this website on our own hardware and network infrastructure, so there are no "hosting companies" or "cloud providers" involved with the operation of this website.</p>
|
||||
|
||||
<br>
|
||||
|
||||
<h4>TRUSTED FIRST PARTIES ARE ALSO SECURITY HOLES</h4>
|
||||
|
||||
<ul>
|
||||
<li>Your IP address may be collected in our webserver logs used for sysadmin purposes.</li>
|
||||
<li>Your IP address may be collected in our self-hosted statistics matomo app.</li>
|
||||
</ul>
|
||||
|
||||
<h4>DON'T TRUST US, PROTECT YOUR PRIVACY</h4>
|
||||
<p>Out of respect for the Bitcoin community, this website does not use any first-party cookies, except to store your preferred language setting (if any). However, we do use minimal first-party analytics and logging as needed for the operation of this website, as follows:</p>
|
||||
|
||||
<ul>
|
||||
<li>Use a Tor Browser or a privacy VPN service to hide your IP address from us.</li>
|
||||
<li>Use a self-hosted instance of The Mempool Open Source Project™ on your own hardware.</li>
|
||||
|
||||
<li>We use basic webserver logging (nginx) for sysadmin purposes, which collects your IP address along with the requests you make. These logs are deleted after 10 days, and we do not share this data with any third-party. To conceal your IP address from our webserver logs, we recommend that you use Tor Browser with our Tor v3 hidden service onion hostname.</li>
|
||||
|
||||
<br>
|
||||
|
||||
<li>We use a self-hosted statistics application (matomo) for analytics purposes, which collects your IP address along with the requests you make. Our matomo instance is configured to respect your privacy by redacting your IP address and other methods, and we do not share this data with any third-party. To conceal your activity from our analytics, we recommend that you use a privacy protecting browser extension that blocks matomo from being loaded.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h4>IF YOU DONATE TO MEMPOOL.SPACE</h4>
|
||||
<br>
|
||||
|
||||
<ul>
|
||||
<li>Your payment information, together with your Twitter identity (if provided) will be collected.</li>
|
||||
<li>We display sponsor profiles publicly on <a href="https://mempool.space/about">mempool.space/about</a>.</li>
|
||||
<li>Thank you :)</li>
|
||||
</ul>
|
||||
<h4>TRUST YOUR OWN SELF-HOSTED MEMPOOL EXPLORER</h4>
|
||||
|
||||
<p>For maximum privacy, we recommend that you use your own self-hosted instance of The Mempool Open Source Project™ on your own hardware. You can easily install your own self-hosted instance of this website on a Raspberry Pi using a one-click installation method maintained by various Bitcoin fullnode distributions such as Umbrel, RaspiBlitz, MyNode, and RoninDojo. See our project's GitHub page for more details about self-hosting this website.</p>
|
||||
|
||||
<br>
|
||||
|
||||
<h4>DONATING TO MEMPOOL.SPACE</h4>
|
||||
|
||||
<p>If you donate to mempool.space, your payment information and your Twitter identity (if provided) will be collected in a database, which may be used to publicly display the sponsor profiles on <a href="https://mempool.space/about">mempool.space/about</a>. Thank you for supporting The Mempool Open Source Project.</p>
|
||||
|
||||
<br>
|
||||
|
||||
<p>EOF</p>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="container-xl">
|
||||
<h1 i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</h1>
|
||||
|
||||
<form [formGroup]="pushTxForm" (submit)="pushTxForm.valid && postTx()" novalidate>
|
||||
<div class="mb-3">
|
||||
<textarea formControlName="txHash" class="form-control" rows="5" i18n-placeholder="transaction.hex" placeholder="Transaction Hex"></textarea>
|
||||
</div>
|
||||
<button [disabled]="isLoading" type="submit" class="btn btn-primary mr-2" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</button>
|
||||
<p class="red-color d-inline">{{ error }}</p> <a *ngIf="txId" [routerLink]="['/tx/' | relativeUrl, txId]">{{ txId }}</a>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-push-transaction',
|
||||
templateUrl: './push-transaction.component.html',
|
||||
styleUrls: ['./push-transaction.component.scss']
|
||||
})
|
||||
export class PushTransactionComponent implements OnInit {
|
||||
pushTxForm: FormGroup;
|
||||
error: string = '';
|
||||
txId: string = '';
|
||||
isLoading = false;
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private apiService: ApiService,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.pushTxForm = this.formBuilder.group({
|
||||
txHash: ['', Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
postTx() {
|
||||
this.isLoading = true;
|
||||
this.error = '';
|
||||
this.txId = '';
|
||||
this.apiService.postTransaction$(this.pushTxForm.get('txHash').value)
|
||||
.subscribe((result) => {
|
||||
this.isLoading = false;
|
||||
this.txId = result;
|
||||
this.pushTxForm.reset();
|
||||
},
|
||||
(error) => {
|
||||
if (typeof error.error === 'string') {
|
||||
const matchText = error.error.match('"message":"(.*?)"');
|
||||
this.error = matchText && matchText[1] || error.error;
|
||||
} else if (error.message) {
|
||||
this.error = error.message;
|
||||
}
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Input, AfterViewInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
||||
import * 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) {
|
||||
|
||||
@@ -10,14 +10,14 @@ form {
|
||||
@media (min-width: 576px) {
|
||||
margin-top: 0px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
width: 58.55px;
|
||||
width: 62px;
|
||||
}
|
||||
|
||||
.search-box-container {
|
||||
@@ -37,4 +37,4 @@ form {
|
||||
.btn {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class SearchFormComponent implements OnInit {
|
||||
|
||||
regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100})$/;
|
||||
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
|
||||
regexTransaction = /^[a-fA-F0-9]{64}$/;
|
||||
regexTransaction = /^([a-fA-F0-9]{64}):?(\d+)?$/;
|
||||
regexBlockheight = /^[0-9]+$/;
|
||||
|
||||
@ViewChild('instance', {static: true}) instance: NgbTypeahead;
|
||||
@@ -100,22 +100,23 @@ export class SearchFormComponent implements OnInit {
|
||||
} else if (this.regexBlockhash.test(searchText) || this.regexBlockheight.test(searchText)) {
|
||||
this.navigate('/block/', searchText);
|
||||
} else if (this.regexTransaction.test(searchText)) {
|
||||
const matches = this.regexTransaction.exec(searchText);
|
||||
if (this.network === 'liquid') {
|
||||
if (this.assets[searchText]) {
|
||||
this.navigate('/asset/', searchText);
|
||||
if (this.assets[matches[1]]) {
|
||||
this.navigate('/asset/', matches[1]);
|
||||
}
|
||||
this.electrsApiService.getAsset$(searchText)
|
||||
this.electrsApiService.getAsset$(matches[1])
|
||||
.subscribe(
|
||||
() => { this.navigate('/asset/', searchText); },
|
||||
() => { this.navigate('/asset/', matches[1]); },
|
||||
() => {
|
||||
this.electrsApiService.getBlock$(searchText)
|
||||
this.electrsApiService.getBlock$(matches[1])
|
||||
.subscribe(
|
||||
(block) => { this.navigate('/block/', searchText, { state: { data: { block } } }); },
|
||||
() => { this.navigate('/tx/', searchText); });
|
||||
(block) => { this.navigate('/block/', matches[1], { state: { data: { block } } }); },
|
||||
() => { this.navigate('/tx/', matches[0]); });
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.navigate('/tx/', searchText);
|
||||
this.navigate('/tx/', matches[0]);
|
||||
}
|
||||
} else {
|
||||
this.isSearching = false;
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
<ng-container *ngIf="specialEvent">
|
||||
<div class="pyro">
|
||||
<div class="before"></div>
|
||||
<div class="after"></div>
|
||||
</div>
|
||||
<div class="warning-label">{{ eventName }}</div>
|
||||
</ng-container>
|
||||
|
||||
<div *ngIf="countdown > 0" class="warning-label">{{ eventName }} in {{ countdown | number }} block{{ countdown === 1 ? '' : 's' }}!</div>
|
||||
|
||||
<div id="blockchain-container" dir="ltr">
|
||||
<app-blockchain></app-blockchain>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@use 'sass:math';
|
||||
|
||||
#blockchain-container {
|
||||
position: relative;
|
||||
overflow-x: scroll;
|
||||
@@ -7,5 +9,141 @@
|
||||
}
|
||||
|
||||
#blockchain-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.warning-label {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
padding: 6px 4px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
// Fireworks
|
||||
|
||||
$particles: 50;
|
||||
$width: 500;
|
||||
$height: 500;
|
||||
|
||||
// Create the explosion...
|
||||
$box-shadow: ();
|
||||
$box-shadow2: ();
|
||||
@for $i from 0 through $particles {
|
||||
$box-shadow: $box-shadow,
|
||||
random($width) - math.div($width, 1.2) + px
|
||||
random($height) - math.div($height, 1.2) + px
|
||||
hsl(random(360), 100%, 50%);
|
||||
$box-shadow2: $box-shadow2, 0 0 #fff
|
||||
}
|
||||
@mixin keyframes ($animationName) {
|
||||
@-webkit-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
|
||||
@-moz-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
|
||||
@-o-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
|
||||
@-ms-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
|
||||
@keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin animation-delay ($settings) {
|
||||
-moz-animation-delay: $settings;
|
||||
-webkit-animation-delay: $settings;
|
||||
-o-animation-delay: $settings;
|
||||
-ms-animation-delay: $settings;
|
||||
animation-delay: $settings;
|
||||
}
|
||||
|
||||
@mixin animation-duration ($settings) {
|
||||
-moz-animation-duration: $settings;
|
||||
-webkit-animation-duration: $settings;
|
||||
-o-animation-duration: $settings;
|
||||
-ms-animation-duration: $settings;
|
||||
animation-duration: $settings;
|
||||
}
|
||||
|
||||
@mixin animation ($settings) {
|
||||
-moz-animation: $settings;
|
||||
-webkit-animation: $settings;
|
||||
-o-animation: $settings;
|
||||
-ms-animation: $settings;
|
||||
animation: $settings;
|
||||
}
|
||||
|
||||
@mixin transform ($settings) {
|
||||
transform: $settings;
|
||||
-moz-transform: $settings;
|
||||
-webkit-transform: $settings;
|
||||
-o-transform: $settings;
|
||||
-ms-transform: $settings;
|
||||
}
|
||||
|
||||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pyro > .before, .pyro > .after {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
box-shadow: $box-shadow2;
|
||||
@include animation((1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards));
|
||||
}
|
||||
|
||||
.pyro > .after {
|
||||
@include animation-delay((1.25s, 1.25s, 1.25s));
|
||||
@include animation-duration((1.25s, 1.25s, 6.25s));
|
||||
}
|
||||
|
||||
@include keyframes(bang) {
|
||||
to {
|
||||
box-shadow:$box-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
@include keyframes(gravity) {
|
||||
to {
|
||||
@include transform(translateY(200px));
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include keyframes(position) {
|
||||
0%, 19.9% {
|
||||
margin-top: 10%;
|
||||
margin-left: 40%;
|
||||
}
|
||||
20%, 39.9% {
|
||||
margin-top: 40%;
|
||||
margin-left: 30%;
|
||||
}
|
||||
40%, 59.9% {
|
||||
margin-top: 20%;
|
||||
margin-left: 70%
|
||||
}
|
||||
60%, 79.9% {
|
||||
margin-top: 30%;
|
||||
margin-left: 20%;
|
||||
}
|
||||
80%, 99.9% {
|
||||
margin-top: 30%;
|
||||
margin-left: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,53 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { specialBlocks } from 'src/app/app.constants';
|
||||
import { takeLast } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start',
|
||||
templateUrl: './start.component.html',
|
||||
styleUrls: ['./start.component.scss'],
|
||||
})
|
||||
export class StartComponent {
|
||||
constructor() { }
|
||||
export class StartComponent implements OnInit {
|
||||
interval = 60;
|
||||
colors = ['#5E35B1', '#ffffff'];
|
||||
|
||||
countdown = 0;
|
||||
specialEvent = false;
|
||||
eventName = '';
|
||||
|
||||
constructor(
|
||||
private websocketService: WebsocketService,
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.websocketService.want(['blocks', 'stats', 'mempool-blocks']);
|
||||
this.stateService.blocks$
|
||||
.subscribe((blocks: any) => {
|
||||
if (this.stateService.network !== '') {
|
||||
return;
|
||||
}
|
||||
this.countdown = 0;
|
||||
const block = blocks[0];
|
||||
|
||||
for (const sb in specialBlocks) {
|
||||
const height = parseInt(sb, 10);
|
||||
const diff = height - block.height;
|
||||
if (diff > 0 && diff <= 1008) {
|
||||
this.countdown = diff;
|
||||
this.eventName = specialBlocks[sb].labelEvent;
|
||||
}
|
||||
}
|
||||
if (specialBlocks[block.height]) {
|
||||
this.specialEvent = true;
|
||||
this.eventName = specialBlocks[block.height].labelEventCompleted;
|
||||
setTimeout(() => {
|
||||
this.specialEvent = false;
|
||||
}, 60 * 60 * 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
<div class="container-graph">
|
||||
<div>
|
||||
<div *ngIf="loading" class="loading">
|
||||
<div class="text-center">
|
||||
<h3 i18n="statistics.loading-graphs">Loading graphs...</h3>
|
||||
<br>
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="card mb-3" *ngIf="mempoolStats.length">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-area-chart"></i> <span i18n="statistics.memory-by-vBytes">Mempool by vBytes (sat/vByte)</span>
|
||||
<form [formGroup]="radioGroupForm" class="formRadioGroup" (click)="saveGraphPreference()">
|
||||
<div class="spinner-border text-light bootstrap-spinner" *ngIf="spinnerLoading"></div>
|
||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'2h'" [routerLink]="['/graphs' | relativeUrl]" fragment="2h"> 2H (LIVE)
|
||||
@@ -35,45 +27,42 @@
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'1y'" [routerLink]="['/graphs' | relativeUrl]" fragment="1y"> 1Y
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'2y'" [routerLink]="['/graphs' | relativeUrl]" fragment="2y"> 2Y
|
||||
</label>
|
||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||
<input ngbButton type="radio" [value]="'3y'" [routerLink]="['/graphs' | relativeUrl]" fragment="3y"> 3Y
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="d-inline-block" ngbDropdown #myDrop="ngbDropdown">
|
||||
<button class="btn btn-primary btn-sm ml-2" id="dropdownFees" ngbDropdownAnchor (click)="myDrop.toggle()">
|
||||
<fa-icon [icon]="['fas', 'filter']" [fixedWidth]="true" i18n-title="statistics.component-filter.title" title="Filter"></fa-icon>
|
||||
</button>
|
||||
<div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees">
|
||||
<ul>
|
||||
<ng-template ngFor let-fee let-i="index" [ngForOf]="feeLevels">
|
||||
<ng-template [ngIf]="fee === 1">
|
||||
<li (click)="filterFees(fee)" [class]="filterFeeIndex > fee ? 'inactive' : ''">
|
||||
<div class="small-buttons">
|
||||
<div ngbDropdown #myDrop="ngbDropdown">
|
||||
<button class="btn btn-primary btn-sm" id="dropdownFees" ngbDropdownAnchor (click)="myDrop.toggle()">
|
||||
<fa-icon [icon]="['fas', 'filter']" [fixedWidth]="true" i18n-title="statistics.component-filter.title" title="Filter"></fa-icon>
|
||||
</button>
|
||||
<div class="dropdown-fees" ngbDropdownMenu aria-labelledby="dropdownFees">
|
||||
<ul>
|
||||
<ng-template ngFor let-fee let-i="index" [ngForOf]="feeLevels">
|
||||
<ng-template [ngIf]="fee <= 400">
|
||||
<li (click)="filterFees(fee)" [class]="filterFeeIndex > fee ? 'inactive' : ''">
|
||||
<ng-template [ngIf]="inverted">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': chartColors[i]}"></span>
|
||||
<span class="fee-text" >{{feeLevels[i]}} - {{ feeLevels[i + 1] }}</span>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="!inverted">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': chartColors[i - 1]}"></span>
|
||||
<span class="fee-text" >{{feeLevels[i]}} - {{ feeLevels[i - 1] }}</span>
|
||||
</ng-template>
|
||||
<span class="fee-text" >0 - {{ fee }}</span>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="fee <= 500 && fee !== 1">
|
||||
<li (click)="filterFees(fee)" [class]="filterFeeIndex > fee ? 'inactive' : ''">
|
||||
<ng-template [ngIf]="inverted">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': chartColors[i]}"></span>
|
||||
<span class="fee-text" >{{feeLevels[i - 1]}} - {{ fee }}</span>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="!inverted">
|
||||
<span class="square" [ngStyle]="{'backgroundColor': chartColors[i - 1]}"></span>
|
||||
<span class="fee-text" >{{feeLevels[i + 1]}} - {{ fee }}</span>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button (click)="invertGraph()" class="btn btn-primary btn-sm ml-2 d-none d-md-inline"><fa-icon [icon]="['fas', 'exchange-alt']" [rotate]="90" [fixedWidth]="true" i18n-title="statistics.component-invert.title" title="Invert"></fa-icon></button>
|
||||
<button (click)="invertGraph()" class="btn btn-primary btn-sm"><fa-icon [icon]="['fas', 'exchange-alt']" [rotate]="90" [fixedWidth]="true" i18n-title="statistics.component-invert.title" title="Invert"></fa-icon></button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="spinner-border text-light bootstrap-spinner" *ngIf="spinnerLoading && mempoolStats.length"></div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="incoming-transactions-graph">
|
||||
@@ -85,7 +74,7 @@
|
||||
[height]="500"
|
||||
[left]="65"
|
||||
[right]="10"
|
||||
[data]="mempoolStats"
|
||||
[data]="mempoolStats && mempoolStats.length ? mempoolStats : null"
|
||||
></app-mempool-graph>
|
||||
</div>
|
||||
</div>
|
||||
@@ -93,7 +82,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="card mb-3" *ngIf="mempoolTransactionsWeightPerSecondData">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-area-chart"></i> <span i18n="statistics.transaction-vbytes-per-second">Transaction vBytes per second (vB/s)</span>
|
||||
</div>
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
.bootstrap-spinner {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-right: 10px;
|
||||
order: 2;
|
||||
margin-left: 10px;
|
||||
@media (min-width: 653px) {
|
||||
margin-left: 0px;
|
||||
order: 1;
|
||||
position: absolute;
|
||||
top: 17px;
|
||||
right: 25px;
|
||||
@media (min-width: 830px) {
|
||||
position: relative;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,17 +36,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.formRadioGroup{
|
||||
.formRadioGroup {
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
@media (min-width: 653px) {
|
||||
display: block;
|
||||
flex-direction: column;
|
||||
@media (min-width: 830px) {
|
||||
flex-direction: row;
|
||||
float: right;
|
||||
margin-top: 0px;
|
||||
}
|
||||
.btn-sm {
|
||||
font-size: 10px;
|
||||
@media (min-width: 768px) {
|
||||
font-size: 9px;
|
||||
@media (min-width: 830px) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
@@ -106,3 +109,48 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group-toggle {
|
||||
display: inline-flex;
|
||||
@media (min-width: 830px) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.small-buttons {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
margin: 7px 0px;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
@media (min-width: 830px) {
|
||||
margin: 2px 0px;
|
||||
width: auto;
|
||||
flex-direction: row;
|
||||
}
|
||||
@media (min-width: 830px) {
|
||||
margin: 0px 0px;
|
||||
}
|
||||
.btn {
|
||||
width: 49.25%;
|
||||
@media (min-width: 830px) {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
.dropdown {
|
||||
width: 49.25%;
|
||||
display: flex;
|
||||
@media (min-width: 830px) {
|
||||
width: auto;
|
||||
margin: 0px 5px;
|
||||
}
|
||||
}
|
||||
#dropdownFees {
|
||||
width: 100%;
|
||||
@media (min-width: 830px) {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export class StatisticsComponent implements OnInit {
|
||||
this.route
|
||||
.fragment
|
||||
.subscribe((fragment) => {
|
||||
if (['2h', '24h', '1w', '1m', '3m', '6m', '1y'].indexOf(fragment) > -1) {
|
||||
if (['2h', '24h', '1w', '1m', '3m', '6m', '1y', '2y', '3y'].indexOf(fragment) > -1) {
|
||||
this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false });
|
||||
}
|
||||
});
|
||||
@@ -104,7 +104,13 @@ export class StatisticsComponent implements OnInit {
|
||||
if (this.radioGroupForm.controls.dateSpan.value === '6m') {
|
||||
return this.apiService.list6MStatistics$();
|
||||
}
|
||||
return this.apiService.list1YStatistics$();
|
||||
if (this.radioGroupForm.controls.dateSpan.value === '1y') {
|
||||
return this.apiService.list1YStatistics$();
|
||||
}
|
||||
if (this.radioGroupForm.controls.dateSpan.value === '2y') {
|
||||
return this.apiService.list2YStatistics$();
|
||||
}
|
||||
return this.apiService.list3YStatistics$();
|
||||
})
|
||||
)
|
||||
.subscribe((mempoolStats: any) => {
|
||||
@@ -128,7 +134,7 @@ export class StatisticsComponent implements OnInit {
|
||||
|
||||
this.mempoolTransactionsWeightPerSecondData = {
|
||||
labels: labels,
|
||||
series: [mempoolStats.map((stats) => stats.vbytes_per_second)],
|
||||
series: [mempoolStats.map((stats) => [stats.added * 1000, stats.vbytes_per_second])],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<div id="tv-wrapper">
|
||||
|
||||
<div *ngIf="mempoolStats.length === 0" class="loading">
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
||||
<div class="tv-container" *ngIf="mempoolStats.length">
|
||||
<div class="tv-container">
|
||||
<div class="chart-holder">
|
||||
<app-mempool-graph
|
||||
[template]="'advanced'"
|
||||
@@ -12,7 +7,7 @@
|
||||
[height]="600"
|
||||
[left]="60"
|
||||
[right]="10"
|
||||
[data]="mempoolStats"
|
||||
[data]="mempoolStats && mempoolStats.length ? mempoolStats : null"
|
||||
[showZoom]="false"
|
||||
></app-mempool-graph>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
}
|
||||
|
||||
.chart-holder {
|
||||
height: 650px;
|
||||
position: relative;
|
||||
height: 655px;
|
||||
width: 100%;
|
||||
margin: 30px auto 0;
|
||||
}
|
||||
|
||||
@@ -9,18 +9,16 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="title">
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
</div>
|
||||
<ng-container>
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
|
||||
<div class="tx-link float-left">
|
||||
<span class="tx-link float-left">
|
||||
<a [routerLink]="['/tx/' | relativeUrl, txId]">
|
||||
<span class="d-inline d-lg-none">{{ txId | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ txId }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="txId"></app-clipboard>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<div class="container-buttons">
|
||||
<ng-template [ngIf]="tx?.status?.confirmed">
|
||||
@@ -34,7 +32,7 @@
|
||||
<button type="button" class="btn btn-sm btn-danger" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -192,36 +190,58 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2 class="float-left" i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
<div class="title float-left">
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-outline-info btn-sm float-right mr-1 mt-0 mt-md-2" (click)="txList.toggleDetails()" i18n="transaction.details|Transaction Details">Details</button>
|
||||
<button type="button" class="btn btn-outline-info details-button btn-sm float-right" (click)="txList.toggleDetails()" i18n="transaction.details|Transaction Details">Details</button>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<app-transactions-list #txList [transactions]="[tx]" [errorUnblinded]="errorUnblinded" [transactionPage]="true"></app-transactions-list>
|
||||
<app-transactions-list #txList [transactions]="[tx]" [errorUnblinded]="errorUnblinded" [outputIndex]="outputIndex" [transactionPage]="true"></app-transactions-list>
|
||||
|
||||
<h2 class="text-left" i18n="transaction.details">Details</h2>
|
||||
<div class="title">
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
</div>
|
||||
<div class="box">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="transaction.size|Transaction Size">Size</td>
|
||||
<td [innerHTML]="'‎' + (tx.size | bytes: 2)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.vsize|Transaction Virtual Size">Virtual size</td>
|
||||
<td [innerHTML]="'‎' + (tx.weight / 4 | vbytes: 2)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.weight|Transaction Weight">Weight</td>
|
||||
<td [innerHTML]="'‎' + (tx.weight | wuBytes: 2)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.hex">Transaction Hex</td>
|
||||
<td><a target="_blank" href="{{ network === '' ? '' : '/' + network }}/api/tx/{{ txId }}/hex"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true"></fa-icon></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="transaction.size|Transaction Size">Size</td>
|
||||
<td [innerHTML]="'‎' + (tx.size | bytes: 2)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.vsize|Transaction Virtual Size">Virtual size</td>
|
||||
<td [innerHTML]="'‎' + (tx.weight / 4 | vbytes: 2)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.weight|Transaction Weight">Weight</td>
|
||||
<td [innerHTML]="'‎' + (tx.weight | wuBytes: 2)"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="transaction.version">Version</td>
|
||||
<td [innerHTML]="'‎' + (tx.version | number)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.locktime">Locktime</td>
|
||||
<td [innerHTML]="'‎' + (tx.locktime | number)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.hex">Transaction Hex</td>
|
||||
<td><a target="_blank" href="{{ network === '' ? '' : '/' + network }}/api/tx/{{ txId }}/hex"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true"></fa-icon></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
@@ -263,41 +283,78 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2>Inputs & Outputs</h2>
|
||||
<div class="title">
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="row">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
<div class="title">
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
|
||||
@@ -2,53 +2,38 @@
|
||||
padding: 0.55rem;
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
color: #FFF;
|
||||
padding-top: 20px;
|
||||
border-top: 3px solid #FFF;
|
||||
width: 100%;
|
||||
padding-bottom: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
@media (min-width: 768px) {
|
||||
padding-bottom: 0px;
|
||||
flex-wrap: wrap;
|
||||
@media (min-width: 650px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
h1 {
|
||||
margin: 0rem;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
h1{
|
||||
margin-top: 2px;
|
||||
margin-bottom: 0;
|
||||
float: left;
|
||||
margin-top: 2px;
|
||||
@media (min-width: 768px){
|
||||
margin-top: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.container-buttons {
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
@media (min-width: 850px) {
|
||||
width: auto;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.tx-link {
|
||||
display: block;
|
||||
width: auto;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 2px;
|
||||
margin-top: 40px;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 8px;
|
||||
@media (min-width: 650px) {
|
||||
align-self: end;
|
||||
margin-left: 15px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
margin-top: 14px;
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: auto;
|
||||
float: left;
|
||||
margin-bottom: 0px;
|
||||
top: 1px;
|
||||
position: relative;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
order: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +62,6 @@ h1{
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.container-xl {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
@@ -119,7 +100,7 @@ h1{
|
||||
}
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
@media (min-width: 768px) {
|
||||
@media (min-width: 850px) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
@@ -129,9 +110,32 @@ h1{
|
||||
}
|
||||
}
|
||||
|
||||
.effective-fee-container{
|
||||
.effective-fee-container {
|
||||
display: block;
|
||||
@media (min-width: 768px){
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
h2 {
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline-info {
|
||||
margin-top: 5px;
|
||||
@media (min-width: 768px){
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.details-button {
|
||||
margin-top: -5px;
|
||||
@media (min-width: 768px){
|
||||
display: inline-block;
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
now = new Date().getTime();
|
||||
timeAvg$: Observable<number>;
|
||||
liquidUnblinding = new LiquidUnblinding();
|
||||
outputIndex: number;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@@ -125,7 +126,9 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
this.subscription = this.route.paramMap
|
||||
.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
this.txId = params.get('id') || '';
|
||||
const urlMatch = (params.get('id') || '').split(':');
|
||||
this.txId = urlMatch[0];
|
||||
this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10);
|
||||
this.seoService.setTitle(
|
||||
$localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`
|
||||
);
|
||||
@@ -163,7 +166,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
|
||||
this.errorUnblinded = error;
|
||||
return of(tx);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
return of(tx);
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user