Merge branch 'master' into mononaut/hide-arrow-on-replace

This commit is contained in:
softsimon 2023-11-11 11:57:03 +09:00 committed by GitHub
commit 874d2b371d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 2064 additions and 1312 deletions

View File

@ -9,7 +9,7 @@ jobs:
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
strategy: strategy:
matrix: matrix:
node: ["16", "17", "18", "20"] node: ["18", "20"]
flavor: ["dev", "prod"] flavor: ["dev", "prod"]
fail-fast: false fail-fast: false
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
@ -67,7 +67,7 @@ jobs:
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
strategy: strategy:
matrix: matrix:
node: ["16", "17", "18", "20"] node: ["18", "20"]
flavor: ["dev", "prod"] flavor: ["dev", "prod"]
fail-fast: false fail-fast: false
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"

View File

@ -38,7 +38,7 @@ jobs:
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 20
cache: "npm" cache: "npm"
cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json

View File

@ -68,17 +68,17 @@ jobs:
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Checkout project - name: Checkout project
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Init repo for Dockerization - name: Init repo for Dockerization
run: docker/init.sh "$TAG" run: docker/init.sh "$TAG"
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3
id: qemu id: qemu
- name: Setup Docker buildx action - name: Setup Docker buildx action
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
id: buildx id: buildx
- name: Available platforms - name: Available platforms
@ -98,7 +98,7 @@ jobs:
docker buildx build \ docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \ --cache-from "type=local,src=/tmp/.buildx-cache" \
--cache-to "type=local,dest=/tmp/.buildx-cache" \ --cache-to "type=local,dest=/tmp/.buildx-cache" \
--platform linux/amd64,linux/arm64,linux/arm/v7 \ --platform linux/amd64,linux/arm64 \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \ --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:$TAG \
--tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \ --tag ${{ secrets.DOCKER_HUB_USER }}/${{ matrix.service }}:latest \
--output "type=registry" ./${{ matrix.service }}/ \ --output "type=registry" ./${{ matrix.service }}/ \

2
.nvmrc
View File

@ -1 +1 @@
v16.16.0 v20.8.0

View File

@ -1,47 +0,0 @@
# If you see pwd_unknown showing up check permissions
PWD ?= pwd_unknown
# DATABASE DEPLOY FOLDER CONFIG - default ./data
ifeq ($(data),)
DATA := data
export DATA
else
DATA := $(data)
export DATA
endif
.PHONY: help
help:
@echo ''
@echo ''
@echo ' Usage: make [COMMAND]'
@echo ''
@echo ' make all # build init mempool and electrs'
@echo ' make init # setup some useful configs'
@echo ' make mempool # build q dockerized mempool.space'
@echo ' make electrs # build a docker electrs image'
@echo ''
.PHONY: init
init:
@echo ''
mkdir -p $(DATA) $(DATA)/mysql $(DATA)/mysql/data
#REF: https://github.com/mempool/mempool/blob/master/docker/README.md
cat docker/docker-compose.yml > docker-compose.yml
cat backend/mempool-config.sample.json > backend/mempool-config.json
.PHONY: mempool
mempool: init
@echo ''
docker-compose up --force-recreate --always-recreate-deps
@echo ''
.PHONY: electrs
electrum:
#REF: https://hub.docker.com/r/beli/electrum
@echo ''
docker build -f docker/electrum/Dockerfile .
@echo ''
.PHONY: all
all: init
make mempool
#######################
-include Makefile

View File

@ -1 +0,0 @@
# For additional configs/scripting

View File

@ -23,6 +23,7 @@ Mempool can be conveniently installed on the following full-node distros:
- [RoninDojo](https://code.samourai.io/ronindojo/RoninDojo) - [RoninDojo](https://code.samourai.io/ronindojo/RoninDojo)
- [myNode](https://github.com/mynodebtc/mynode) - [myNode](https://github.com/mynodebtc/mynode)
- [Start9](https://github.com/Start9Labs/embassy-os) - [Start9](https://github.com/Start9Labs/embassy-os)
- [nix-bitcoin](https://github.com/fort-nix/nix-bitcoin/blob/a1eacce6768ca4894f365af8f79be5bbd594e1c3/examples/configuration.nix#L129)
**We highly recommend you deploy your own Mempool instance this way.** No matter which option you pick, you'll be able to get your own fully-sovereign instance of Mempool up quickly without needing to fiddle with any settings. **We highly recommend you deploy your own Mempool instance this way.** No matter which option you pick, you'll be able to get your own fully-sovereign instance of Mempool up quickly without needing to fiddle with any settings.

View File

@ -51,6 +51,8 @@
"REST_API_URL": "http://127.0.0.1:3000", "REST_API_URL": "http://127.0.0.1:3000",
"UNIX_SOCKET_PATH": "/tmp/esplora-bitcoin-mainnet", "UNIX_SOCKET_PATH": "/tmp/esplora-bitcoin-mainnet",
"RETRY_UNIX_SOCKET_AFTER": 30000, "RETRY_UNIX_SOCKET_AFTER": 30000,
"REQUEST_TIMEOUT": 10000,
"FALLBACK_TIMEOUT": 5000,
"FALLBACK": [] "FALLBACK": []
}, },
"SECOND_CORE_RPC": { "SECOND_CORE_RPC": {
@ -68,7 +70,8 @@
"DATABASE": "mempool", "DATABASE": "mempool",
"USERNAME": "mempool", "USERNAME": "mempool",
"PASSWORD": "mempool", "PASSWORD": "mempool",
"TIMEOUT": 180000 "TIMEOUT": 180000,
"PID_DIR": ""
}, },
"SYSLOG": { "SYSLOG": {
"ENABLED": true, "ENABLED": true,

View File

@ -9,12 +9,12 @@
"version": "3.0.0-dev", "version": "3.0.0-dev",
"license": "GNU Affero General Public License v3.0", "license": "GNU Affero General Public License v3.0",
"dependencies": { "dependencies": {
"@babel/core": "^7.21.3", "@babel/core": "^7.23.2",
"@mempool/electrum-client": "1.1.9", "@mempool/electrum-client": "1.1.9",
"@types/node": "^18.15.3", "@types/node": "^18.15.3",
"axios": "~1.5.0", "axios": "~1.6.1",
"bitcoinjs-lib": "~6.1.3", "bitcoinjs-lib": "~6.1.3",
"crypto-js": "~4.1.1", "crypto-js": "~4.2.0",
"express": "~4.18.2", "express": "~4.18.2",
"maxmind": "~4.3.11", "maxmind": "~4.3.11",
"mysql2": "~3.6.0", "mysql2": "~3.6.0",
@ -26,7 +26,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
"@babel/core": "^7.21.3", "@babel/core": "^7.23.2",
"@types/compression": "^1.7.2", "@types/compression": "^1.7.2",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
@ -65,47 +65,48 @@
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.21.4", "version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/highlight": "^7.18.6" "@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/compat-data": { "node_modules/@babel/compat-data": {
"version": "7.21.4", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz",
"integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/core": { "node_modules/@babel/core": {
"version": "7.21.4", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz",
"integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.2.0", "@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.21.4", "@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.21.4", "@babel/generator": "^7.23.0",
"@babel/helper-compilation-targets": "^7.21.4", "@babel/helper-compilation-targets": "^7.22.15",
"@babel/helper-module-transforms": "^7.21.2", "@babel/helper-module-transforms": "^7.23.0",
"@babel/helpers": "^7.21.0", "@babel/helpers": "^7.23.2",
"@babel/parser": "^7.21.4", "@babel/parser": "^7.23.0",
"@babel/template": "^7.20.7", "@babel/template": "^7.22.15",
"@babel/traverse": "^7.21.4", "@babel/traverse": "^7.23.2",
"@babel/types": "^7.21.4", "@babel/types": "^7.23.0",
"convert-source-map": "^1.7.0", "convert-source-map": "^2.0.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"gensync": "^1.0.0-beta.2", "gensync": "^1.0.0-beta.2",
"json5": "^2.2.2", "json5": "^2.2.3",
"semver": "^6.3.0" "semver": "^6.3.1"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -115,13 +116,19 @@
"url": "https://opencollective.com/babel" "url": "https://opencollective.com/babel"
} }
}, },
"node_modules/@babel/core/node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true
},
"node_modules/@babel/generator": { "node_modules/@babel/generator": {
"version": "7.21.4", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.21.4", "@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17", "@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1" "jsesc": "^2.5.1"
@ -145,87 +152,84 @@
} }
}, },
"node_modules/@babel/helper-compilation-targets": { "node_modules/@babel/helper-compilation-targets": {
"version": "7.21.4", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
"integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/compat-data": "^7.21.4", "@babel/compat-data": "^7.22.9",
"@babel/helper-validator-option": "^7.21.0", "@babel/helper-validator-option": "^7.22.15",
"browserslist": "^4.21.3", "browserslist": "^4.21.9",
"lru-cache": "^5.1.1", "lru-cache": "^5.1.1",
"semver": "^6.3.0" "semver": "^6.3.1"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
} }
}, },
"node_modules/@babel/helper-environment-visitor": { "node_modules/@babel/helper-environment-visitor": {
"version": "7.18.9", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-function-name": { "node_modules/@babel/helper-function-name": {
"version": "7.21.0", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/template": "^7.20.7", "@babel/template": "^7.22.15",
"@babel/types": "^7.21.0" "@babel/types": "^7.23.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-hoist-variables": { "node_modules/@babel/helper-hoist-variables": {
"version": "7.18.6", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.18.6" "@babel/types": "^7.22.5"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-module-imports": { "node_modules/@babel/helper-module-imports": {
"version": "7.21.4", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
"integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.21.4" "@babel/types": "^7.22.15"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-module-transforms": { "node_modules/@babel/helper-module-transforms": {
"version": "7.21.2", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz",
"integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-module-imports": "^7.18.6", "@babel/helper-module-imports": "^7.22.15",
"@babel/helper-simple-access": "^7.20.2", "@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-split-export-declaration": "^7.22.6",
"@babel/helper-validator-identifier": "^7.19.1", "@babel/helper-validator-identifier": "^7.22.20"
"@babel/template": "^7.20.7",
"@babel/traverse": "^7.21.2",
"@babel/types": "^7.21.2"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
} }
}, },
"node_modules/@babel/helper-plugin-utils": { "node_modules/@babel/helper-plugin-utils": {
@ -238,78 +242,78 @@
} }
}, },
"node_modules/@babel/helper-simple-access": { "node_modules/@babel/helper-simple-access": {
"version": "7.20.2", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
"integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.20.2" "@babel/types": "^7.22.5"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-split-export-declaration": { "node_modules/@babel/helper-split-export-declaration": {
"version": "7.18.6", "version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.18.6" "@babel/types": "^7.22.5"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.19.4", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-identifier": { "node_modules/@babel/helper-validator-identifier": {
"version": "7.19.1", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-option": { "node_modules/@babel/helper-validator-option": {
"version": "7.21.0", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz",
"integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helpers": { "node_modules/@babel/helpers": {
"version": "7.21.0", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz",
"integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/template": "^7.20.7", "@babel/template": "^7.22.15",
"@babel/traverse": "^7.21.0", "@babel/traverse": "^7.23.2",
"@babel/types": "^7.21.0" "@babel/types": "^7.23.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/highlight": { "node_modules/@babel/highlight": {
"version": "7.18.6", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.18.6", "@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.0.0", "chalk": "^2.4.2",
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
}, },
"engines": { "engines": {
@ -317,9 +321,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.21.4", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"dev": true, "dev": true,
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
@ -506,33 +510,33 @@
} }
}, },
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.20.7", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.20.7", "@babel/parser": "^7.22.15",
"@babel/types": "^7.20.7" "@babel/types": "^7.22.15"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/traverse": { "node_modules/@babel/traverse": {
"version": "7.21.4", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.21.4", "@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.21.4", "@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.21.0", "@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.21.4", "@babel/parser": "^7.23.0",
"@babel/types": "^7.21.4", "@babel/types": "^7.23.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"globals": "^11.1.0" "globals": "^11.1.0"
}, },
@ -541,13 +545,13 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.21.4", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.19.4", "@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.19.1", "@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
}, },
"engines": { "engines": {
@ -2321,9 +2325,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.5.0", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz",
"integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@ -2590,9 +2594,9 @@
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.21.5", "version": "4.22.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
"integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -2602,13 +2606,17 @@
{ {
"type": "tidelift", "type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist" "url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
} }
], ],
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001449", "caniuse-lite": "^1.0.30001541",
"electron-to-chromium": "^1.4.284", "electron-to-chromium": "^1.4.535",
"node-releases": "^2.0.8", "node-releases": "^2.0.13",
"update-browserslist-db": "^1.0.10" "update-browserslist-db": "^1.0.13"
}, },
"bin": { "bin": {
"browserslist": "cli.js" "browserslist": "cli.js"
@ -2700,9 +2708,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001473", "version": "1.0.30001547",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz",
"integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -2892,9 +2900,9 @@
} }
}, },
"node_modules/crypto-js": { "node_modules/crypto-js": {
"version": "4.1.1", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.4", "version": "4.3.4",
@ -3023,9 +3031,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.348", "version": "1.4.551",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz",
"integrity": "sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ==", "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==",
"dev": true "dev": true
}, },
"node_modules/emittery": { "node_modules/emittery": {
@ -6184,9 +6192,9 @@
"dev": true "dev": true
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.10", "version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
"dev": true "dev": true
}, },
"node_modules/normalize-path": { "node_modules/normalize-path": {
@ -7399,9 +7407,9 @@
} }
}, },
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.0.10", "version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -7411,6 +7419,10 @@
{ {
"type": "tidelift", "type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist" "url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
} }
], ],
"dependencies": { "dependencies": {
@ -7418,7 +7430,7 @@
"picocolors": "^1.0.0" "picocolors": "^1.0.0"
}, },
"bin": { "bin": {
"browserslist-lint": "cli.js" "update-browserslist-db": "cli.js"
}, },
"peerDependencies": { "peerDependencies": {
"browserslist": ">= 4.21.0" "browserslist": ">= 4.21.0"
@ -7683,50 +7695,59 @@
} }
}, },
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.21.4", "version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/highlight": "^7.18.6" "@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
} }
}, },
"@babel/compat-data": { "@babel/compat-data": {
"version": "7.21.4", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz",
"integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==",
"dev": true "dev": true
}, },
"@babel/core": { "@babel/core": {
"version": "7.21.4", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz",
"integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@ampproject/remapping": "^2.2.0", "@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.21.4", "@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.21.4", "@babel/generator": "^7.23.0",
"@babel/helper-compilation-targets": "^7.21.4", "@babel/helper-compilation-targets": "^7.22.15",
"@babel/helper-module-transforms": "^7.21.2", "@babel/helper-module-transforms": "^7.23.0",
"@babel/helpers": "^7.21.0", "@babel/helpers": "^7.23.2",
"@babel/parser": "^7.21.4", "@babel/parser": "^7.23.0",
"@babel/template": "^7.20.7", "@babel/template": "^7.22.15",
"@babel/traverse": "^7.21.4", "@babel/traverse": "^7.23.2",
"@babel/types": "^7.21.4", "@babel/types": "^7.23.0",
"convert-source-map": "^1.7.0", "convert-source-map": "^2.0.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"gensync": "^1.0.0-beta.2", "gensync": "^1.0.0-beta.2",
"json5": "^2.2.2", "json5": "^2.2.3",
"semver": "^6.3.0" "semver": "^6.3.1"
},
"dependencies": {
"convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true
}
} }
}, },
"@babel/generator": { "@babel/generator": {
"version": "7.21.4", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "^7.21.4", "@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17", "@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1" "jsesc": "^2.5.1"
@ -7746,66 +7767,63 @@
} }
}, },
"@babel/helper-compilation-targets": { "@babel/helper-compilation-targets": {
"version": "7.21.4", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
"integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/compat-data": "^7.21.4", "@babel/compat-data": "^7.22.9",
"@babel/helper-validator-option": "^7.21.0", "@babel/helper-validator-option": "^7.22.15",
"browserslist": "^4.21.3", "browserslist": "^4.21.9",
"lru-cache": "^5.1.1", "lru-cache": "^5.1.1",
"semver": "^6.3.0" "semver": "^6.3.1"
} }
}, },
"@babel/helper-environment-visitor": { "@babel/helper-environment-visitor": {
"version": "7.18.9", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"dev": true "dev": true
}, },
"@babel/helper-function-name": { "@babel/helper-function-name": {
"version": "7.21.0", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/template": "^7.20.7", "@babel/template": "^7.22.15",
"@babel/types": "^7.21.0" "@babel/types": "^7.23.0"
} }
}, },
"@babel/helper-hoist-variables": { "@babel/helper-hoist-variables": {
"version": "7.18.6", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "^7.18.6" "@babel/types": "^7.22.5"
} }
}, },
"@babel/helper-module-imports": { "@babel/helper-module-imports": {
"version": "7.21.4", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
"integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "^7.21.4" "@babel/types": "^7.22.15"
} }
}, },
"@babel/helper-module-transforms": { "@babel/helper-module-transforms": {
"version": "7.21.2", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz",
"integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-module-imports": "^7.18.6", "@babel/helper-module-imports": "^7.22.15",
"@babel/helper-simple-access": "^7.20.2", "@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-split-export-declaration": "^7.22.6",
"@babel/helper-validator-identifier": "^7.19.1", "@babel/helper-validator-identifier": "^7.22.20"
"@babel/template": "^7.20.7",
"@babel/traverse": "^7.21.2",
"@babel/types": "^7.21.2"
} }
}, },
"@babel/helper-plugin-utils": { "@babel/helper-plugin-utils": {
@ -7815,67 +7833,67 @@
"dev": true "dev": true
}, },
"@babel/helper-simple-access": { "@babel/helper-simple-access": {
"version": "7.20.2", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
"integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "^7.20.2" "@babel/types": "^7.22.5"
} }
}, },
"@babel/helper-split-export-declaration": { "@babel/helper-split-export-declaration": {
"version": "7.18.6", "version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "^7.18.6" "@babel/types": "^7.22.5"
} }
}, },
"@babel/helper-string-parser": { "@babel/helper-string-parser": {
"version": "7.19.4", "version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true "dev": true
}, },
"@babel/helper-validator-identifier": { "@babel/helper-validator-identifier": {
"version": "7.19.1", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"dev": true "dev": true
}, },
"@babel/helper-validator-option": { "@babel/helper-validator-option": {
"version": "7.21.0", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz",
"integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==",
"dev": true "dev": true
}, },
"@babel/helpers": { "@babel/helpers": {
"version": "7.21.0", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz",
"integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/template": "^7.20.7", "@babel/template": "^7.22.15",
"@babel/traverse": "^7.21.0", "@babel/traverse": "^7.23.2",
"@babel/types": "^7.21.0" "@babel/types": "^7.23.0"
} }
}, },
"@babel/highlight": { "@babel/highlight": {
"version": "7.18.6", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-validator-identifier": "^7.18.6", "@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.0.0", "chalk": "^2.4.2",
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.21.4", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"dev": true "dev": true
}, },
"@babel/plugin-syntax-async-generators": { "@babel/plugin-syntax-async-generators": {
@ -8005,42 +8023,42 @@
} }
}, },
"@babel/template": { "@babel/template": {
"version": "7.20.7", "version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.20.7", "@babel/parser": "^7.22.15",
"@babel/types": "^7.20.7" "@babel/types": "^7.22.15"
} }
}, },
"@babel/traverse": { "@babel/traverse": {
"version": "7.21.4", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.21.4", "@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.21.4", "@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.21.0", "@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.21.4", "@babel/parser": "^7.23.0",
"@babel/types": "^7.21.4", "@babel/types": "^7.23.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"globals": "^11.1.0" "globals": "^11.1.0"
} }
}, },
"@babel/types": { "@babel/types": {
"version": "7.21.4", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-string-parser": "^7.19.4", "@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.19.1", "@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
}, },
@ -9397,9 +9415,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
}, },
"axios": { "axios": {
"version": "1.5.0", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz",
"integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==",
"requires": { "requires": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@ -9615,15 +9633,15 @@
} }
}, },
"browserslist": { "browserslist": {
"version": "4.21.5", "version": "4.22.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
"integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"caniuse-lite": "^1.0.30001449", "caniuse-lite": "^1.0.30001541",
"electron-to-chromium": "^1.4.284", "electron-to-chromium": "^1.4.535",
"node-releases": "^2.0.8", "node-releases": "^2.0.13",
"update-browserslist-db": "^1.0.10" "update-browserslist-db": "^1.0.13"
} }
}, },
"bs-logger": { "bs-logger": {
@ -9694,9 +9712,9 @@
"dev": true "dev": true
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001473", "version": "1.0.30001547",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz",
"integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==",
"dev": true "dev": true
}, },
"chalk": { "chalk": {
@ -9832,9 +9850,9 @@
} }
}, },
"crypto-js": { "crypto-js": {
"version": "4.1.1", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
}, },
"debug": { "debug": {
"version": "4.3.4", "version": "4.3.4",
@ -9924,9 +9942,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.4.348", "version": "1.4.551",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz",
"integrity": "sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ==", "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==",
"dev": true "dev": true
}, },
"emittery": { "emittery": {
@ -12280,9 +12298,9 @@
"dev": true "dev": true
}, },
"node-releases": { "node-releases": {
"version": "2.0.10", "version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
"dev": true "dev": true
}, },
"normalize-path": { "normalize-path": {
@ -13118,9 +13136,9 @@
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
}, },
"update-browserslist-db": { "update-browserslist-db": {
"version": "1.0.10", "version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
"dev": true, "dev": true,
"requires": { "requires": {
"escalade": "^3.1.1", "escalade": "^3.1.1",

View File

@ -38,12 +38,12 @@
"rust-build": "cd rust-gbt && npm run build-release" "rust-build": "cd rust-gbt && npm run build-release"
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.21.3", "@babel/core": "^7.23.2",
"@mempool/electrum-client": "1.1.9", "@mempool/electrum-client": "1.1.9",
"@types/node": "^18.15.3", "@types/node": "^18.15.3",
"axios": "~1.5.0", "axios": "~1.6.1",
"bitcoinjs-lib": "~6.1.3", "bitcoinjs-lib": "~6.1.3",
"crypto-js": "~4.1.1", "crypto-js": "~4.2.0",
"express": "~4.18.2", "express": "~4.18.2",
"maxmind": "~4.3.11", "maxmind": "~4.3.11",
"mysql2": "~3.6.0", "mysql2": "~3.6.0",
@ -55,7 +55,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
"@babel/core": "^7.21.3", "@babel/core": "^7.23.2",
"@types/compression": "^1.7.2", "@types/compression": "^1.7.2",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",

View File

@ -52,6 +52,8 @@
"REST_API_URL": "__ESPLORA_REST_API_URL__", "REST_API_URL": "__ESPLORA_REST_API_URL__",
"UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__", "UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__",
"RETRY_UNIX_SOCKET_AFTER": 888, "RETRY_UNIX_SOCKET_AFTER": 888,
"REQUEST_TIMEOUT": 10000,
"FALLBACK_TIMEOUT": 5000,
"FALLBACK": [] "FALLBACK": []
}, },
"SECOND_CORE_RPC": { "SECOND_CORE_RPC": {
@ -69,6 +71,7 @@
"DATABASE": "__DATABASE_DATABASE__", "DATABASE": "__DATABASE_DATABASE__",
"USERNAME": "__DATABASE_USERNAME__", "USERNAME": "__DATABASE_USERNAME__",
"PASSWORD": "__DATABASE_PASSWORD__", "PASSWORD": "__DATABASE_PASSWORD__",
"PID_DIR": "__DATABASE_PID_FILE__",
"TIMEOUT": 3000 "TIMEOUT": 3000
}, },
"SYSLOG": { "SYSLOG": {

View File

@ -56,6 +56,8 @@ describe('Mempool Backend Config', () => {
REST_API_URL: 'http://127.0.0.1:3000', REST_API_URL: 'http://127.0.0.1:3000',
UNIX_SOCKET_PATH: null, UNIX_SOCKET_PATH: null,
RETRY_UNIX_SOCKET_AFTER: 30000, RETRY_UNIX_SOCKET_AFTER: 30000,
REQUEST_TIMEOUT: 10000,
FALLBACK_TIMEOUT: 5000,
FALLBACK: [], FALLBACK: [],
}); });
@ -84,6 +86,7 @@ describe('Mempool Backend Config', () => {
USERNAME: 'mempool', USERNAME: 'mempool',
PASSWORD: 'mempool', PASSWORD: 'mempool',
TIMEOUT: 180000, TIMEOUT: 180000,
PID_DIR: ''
}); });
expect(config.SYSLOG).toStrictEqual({ expect(config.SYSLOG).toStrictEqual({

View File

@ -478,7 +478,7 @@ class BitcoinRoutes {
} }
let nextHash = startFromHash; let nextHash = startFromHash;
for (let i = 0; i < 10 && nextHash; i++) { for (let i = 0; i < 15 && nextHash; i++) {
const localBlock = blocks.getBlocks().find((b) => b.id === nextHash); const localBlock = blocks.getBlocks().find((b) => b.id === nextHash);
if (localBlock) { if (localBlock) {
returnBlocks.push(localBlock); returnBlocks.push(localBlock);

View File

@ -75,9 +75,9 @@ class FailoverRouter {
const results = await Promise.allSettled(this.hosts.map(async (host) => { const results = await Promise.allSettled(this.hosts.map(async (host) => {
if (host.socket) { if (host.socket) {
return this.pollConnection.get<number>('/blocks/tip/height', { socketPath: host.host, timeout: 5000 }); return this.pollConnection.get<number>('/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT });
} else { } else {
return this.pollConnection.get<number>(host.host + '/blocks/tip/height', { timeout: 5000 }); return this.pollConnection.get<number>(host.host + '/blocks/tip/height', { timeout: config.ESPLORA.FALLBACK_TIMEOUT });
} }
})); }));
const maxHeight = results.reduce((max, result) => Math.max(max, result.status === 'fulfilled' ? result.value?.data || 0 : 0), 0); const maxHeight = results.reduce((max, result) => Math.max(max, result.status === 'fulfilled' ? result.value?.data || 0 : 0), 0);
@ -168,10 +168,10 @@ class FailoverRouter {
let axiosConfig; let axiosConfig;
let url; let url;
if (host.socket) { if (host.socket) {
axiosConfig = { socketPath: host.host, timeout: 10000, responseType }; axiosConfig = { socketPath: host.host, timeout: config.ESPLORA.REQUEST_TIMEOUT, responseType };
url = path; url = path;
} else { } else {
axiosConfig = { timeout: 10000, responseType }; axiosConfig = { timeout: config.ESPLORA.REQUEST_TIMEOUT, responseType };
url = host.host + path; url = host.host + path;
} }
return (method === 'post' return (method === 'post'

View File

@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
import { RowDataPacket } from 'mysql2'; import { RowDataPacket } from 'mysql2';
class DatabaseMigration { class DatabaseMigration {
private static currentVersion = 65; private static currentVersion = 66;
private queryTimeout = 3600_000; private queryTimeout = 3600_000;
private statisticsAddedIndexed = false; private statisticsAddedIndexed = false;
private uniqueLogs: string[] = []; private uniqueLogs: string[] = [];
@ -553,6 +553,11 @@ class DatabaseMigration {
await this.$executeQuery('ALTER TABLE `blocks_audits` ADD accelerated_txs JSON DEFAULT "[]"'); await this.$executeQuery('ALTER TABLE `blocks_audits` ADD accelerated_txs JSON DEFAULT "[]"');
await this.updateToSchemaVersion(65); await this.updateToSchemaVersion(65);
} }
if (databaseSchemaVersion < 66) {
await this.$executeQuery('ALTER TABLE `statistics` ADD min_fee FLOAT UNSIGNED DEFAULT NULL');
await this.updateToSchemaVersion(66);
}
} }
/** /**

View File

@ -3,21 +3,30 @@ import { Common } from './common';
import mempool from './mempool'; import mempool from './mempool';
import projectedBlocks from './mempool-blocks'; import projectedBlocks from './mempool-blocks';
interface RecommendedFees {
fastestFee: number,
halfHourFee: number,
hourFee: number,
economyFee: number,
minimumFee: number,
}
class FeeApi { class FeeApi {
constructor() { } constructor() { }
defaultFee = Common.isLiquid() ? 0.1 : 1; defaultFee = Common.isLiquid() ? 0.1 : 1;
public getRecommendedFee() { public getRecommendedFee(): RecommendedFees {
const pBlocks = projectedBlocks.getMempoolBlocks(); const pBlocks = projectedBlocks.getMempoolBlocks();
const mPool = mempool.getMempoolInfo(); const mPool = mempool.getMempoolInfo();
const minimumFee = Math.ceil(mPool.mempoolminfee * 100000); const minimumFee = Math.ceil(mPool.mempoolminfee * 100000);
const defaultMinFee = Math.max(minimumFee, this.defaultFee);
if (!pBlocks.length) { if (!pBlocks.length) {
return { return {
'fastestFee': this.defaultFee, 'fastestFee': defaultMinFee,
'halfHourFee': this.defaultFee, 'halfHourFee': defaultMinFee,
'hourFee': this.defaultFee, 'hourFee': defaultMinFee,
'economyFee': minimumFee, 'economyFee': minimumFee,
'minimumFee': minimumFee, 'minimumFee': minimumFee,
}; };
@ -27,11 +36,15 @@ class FeeApi {
const secondMedianFee = pBlocks[1] ? this.optimizeMedianFee(pBlocks[1], pBlocks[2], firstMedianFee) : this.defaultFee; const secondMedianFee = pBlocks[1] ? this.optimizeMedianFee(pBlocks[1], pBlocks[2], firstMedianFee) : this.defaultFee;
const thirdMedianFee = pBlocks[2] ? this.optimizeMedianFee(pBlocks[2], pBlocks[3], secondMedianFee) : this.defaultFee; const thirdMedianFee = pBlocks[2] ? this.optimizeMedianFee(pBlocks[2], pBlocks[3], secondMedianFee) : this.defaultFee;
// explicitly enforce a minimum of ceil(mempoolminfee) on all recommendations.
// simply rounding up recommended rates is insufficient, as the purging rate
// can exceed the median rate of projected blocks in some extreme scenarios
// (see https://bitcoin.stackexchange.com/a/120024)
return { return {
'fastestFee': firstMedianFee, 'fastestFee': Math.max(minimumFee, firstMedianFee),
'halfHourFee': secondMedianFee, 'halfHourFee': Math.max(minimumFee, secondMedianFee),
'hourFee': thirdMedianFee, 'hourFee': Math.max(minimumFee, thirdMedianFee),
'economyFee': Math.min(2 * minimumFee, thirdMedianFee), 'economyFee': Math.max(minimumFee, Math.min(2 * minimumFee, thirdMedianFee)),
'minimumFee': minimumFee, 'minimumFee': minimumFee,
}; };
} }

View File

@ -31,7 +31,7 @@ class MemoryCache {
} }
private cleanup() { private cleanup() {
this.cache = this.cache.filter((cache) => cache.expires < (new Date())); this.cache = this.cache.filter((cache) => cache.expires > (new Date()));
} }
} }

View File

@ -15,6 +15,7 @@ class StatisticsApi {
mempool_byte_weight, mempool_byte_weight,
fee_data, fee_data,
total_fee, total_fee,
min_fee,
vsize_1, vsize_1,
vsize_2, vsize_2,
vsize_3, vsize_3,
@ -54,7 +55,7 @@ class StatisticsApi {
vsize_1800, vsize_1800,
vsize_2000 vsize_2000
) )
VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)`; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)`;
const [result]: any = await DB.query(query); const [result]: any = await DB.query(query);
return result.insertId; return result.insertId;
@ -73,6 +74,7 @@ class StatisticsApi {
mempool_byte_weight, mempool_byte_weight,
fee_data, fee_data,
total_fee, total_fee,
min_fee,
vsize_1, vsize_1,
vsize_2, vsize_2,
vsize_3, vsize_3,
@ -112,7 +114,7 @@ class StatisticsApi {
vsize_1800, vsize_1800,
vsize_2000 vsize_2000
) )
VALUES (${statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, VALUES (${statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
const params: (string | number)[] = [ const params: (string | number)[] = [
@ -122,6 +124,7 @@ class StatisticsApi {
statistics.mempool_byte_weight, statistics.mempool_byte_weight,
statistics.fee_data, statistics.fee_data,
statistics.total_fee, statistics.total_fee,
statistics.min_fee,
statistics.vsize_1, statistics.vsize_1,
statistics.vsize_2, statistics.vsize_2,
statistics.vsize_3, statistics.vsize_3,
@ -173,6 +176,7 @@ class StatisticsApi {
UNIX_TIMESTAMP(added) as added, UNIX_TIMESTAMP(added) as added,
CAST(avg(unconfirmed_transactions) as DOUBLE) as unconfirmed_transactions, CAST(avg(unconfirmed_transactions) as DOUBLE) as unconfirmed_transactions,
CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second,
CAST(avg(min_fee) as DOUBLE) as min_fee,
CAST(avg(vsize_1) as DOUBLE) as vsize_1, CAST(avg(vsize_1) as DOUBLE) as vsize_1,
CAST(avg(vsize_2) as DOUBLE) as vsize_2, CAST(avg(vsize_2) as DOUBLE) as vsize_2,
CAST(avg(vsize_3) as DOUBLE) as vsize_3, CAST(avg(vsize_3) as DOUBLE) as vsize_3,
@ -222,6 +226,7 @@ class StatisticsApi {
UNIX_TIMESTAMP(added) as added, UNIX_TIMESTAMP(added) as added,
CAST(avg(unconfirmed_transactions) as DOUBLE) as unconfirmed_transactions, CAST(avg(unconfirmed_transactions) as DOUBLE) as unconfirmed_transactions,
CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second,
CAST(avg(min_fee) as DOUBLE) as min_fee,
vsize_1, vsize_1,
vsize_2, vsize_2,
vsize_3, vsize_3,
@ -407,6 +412,7 @@ class StatisticsApi {
vbytes_per_second: s.vbytes_per_second, vbytes_per_second: s.vbytes_per_second,
mempool_byte_weight: s.mempool_byte_weight, mempool_byte_weight: s.mempool_byte_weight,
total_fee: s.total_fee, total_fee: s.total_fee,
min_fee: s.min_fee,
vsizes: [ vsizes: [
s.vsize_1, s.vsize_1,
s.vsize_2, s.vsize_2,

View File

@ -89,6 +89,9 @@ class Statistics {
} }
}); });
// get minFee and convert to sats/vb
const minFee = memPool.getMempoolInfo().mempoolminfee * 100000;
try { try {
const insertId = await statisticsApi.$create({ const insertId = await statisticsApi.$create({
added: 'NOW()', added: 'NOW()',
@ -98,6 +101,7 @@ class Statistics {
mempool_byte_weight: totalWeight, mempool_byte_weight: totalWeight,
total_fee: totalFee, total_fee: totalFee,
fee_data: '', fee_data: '',
min_fee: minFee,
vsize_1: weightVsizeFees['1'] || 0, vsize_1: weightVsizeFees['1'] || 0,
vsize_2: weightVsizeFees['2'] || 0, vsize_2: weightVsizeFees['2'] || 0,
vsize_3: weightVsizeFees['3'] || 0, vsize_3: weightVsizeFees['3'] || 0,

View File

@ -44,6 +44,8 @@ interface IConfig {
REST_API_URL: string; REST_API_URL: string;
UNIX_SOCKET_PATH: string | void | null; UNIX_SOCKET_PATH: string | void | null;
RETRY_UNIX_SOCKET_AFTER: number; RETRY_UNIX_SOCKET_AFTER: number;
REQUEST_TIMEOUT: number;
FALLBACK_TIMEOUT: number;
FALLBACK: string[]; FALLBACK: string[];
}; };
LIGHTNING: { LIGHTNING: {
@ -93,6 +95,7 @@ interface IConfig {
USERNAME: string; USERNAME: string;
PASSWORD: string; PASSWORD: string;
TIMEOUT: number; TIMEOUT: number;
PID_DIR: string;
}; };
SYSLOG: { SYSLOG: {
ENABLED: boolean; ENABLED: boolean;
@ -189,6 +192,8 @@ const defaults: IConfig = {
'REST_API_URL': 'http://127.0.0.1:3000', 'REST_API_URL': 'http://127.0.0.1:3000',
'UNIX_SOCKET_PATH': null, 'UNIX_SOCKET_PATH': null,
'RETRY_UNIX_SOCKET_AFTER': 30000, 'RETRY_UNIX_SOCKET_AFTER': 30000,
'REQUEST_TIMEOUT': 10000,
'FALLBACK_TIMEOUT': 5000,
'FALLBACK': [], 'FALLBACK': [],
}, },
'ELECTRUM': { 'ELECTRUM': {
@ -219,6 +224,7 @@ const defaults: IConfig = {
'USERNAME': 'mempool', 'USERNAME': 'mempool',
'PASSWORD': 'mempool', 'PASSWORD': 'mempool',
'TIMEOUT': 180000, 'TIMEOUT': 180000,
'PID_DIR': '',
}, },
'SYSLOG': { 'SYSLOG': {
'ENABLED': true, 'ENABLED': true,

View File

@ -1,3 +1,5 @@
import * as fs from 'fs';
import path from 'path';
import config from './config'; import config from './config';
import { createPool, Pool, PoolConnection } from 'mysql2/promise'; import { createPool, Pool, PoolConnection } from 'mysql2/promise';
import logger from './logger'; import logger from './logger';
@ -101,6 +103,33 @@ import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } fr
} }
} }
public getPidLock(): boolean {
const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`);
if (fs.existsSync(filePath)) {
const pid = fs.readFileSync(filePath).toString();
if (pid !== `${process.pid}`) {
const msg = `Already running on PID ${pid} (or pid file '${filePath}' is stale)`;
logger.err(msg);
throw new Error(msg);
} else {
return true;
}
} else {
fs.writeFileSync(filePath, `${process.pid}`);
return true;
}
}
public releasePidLock(): void {
const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`);
if (fs.existsSync(filePath)) {
const pid = fs.readFileSync(filePath).toString();
if (pid === `${process.pid}`) {
fs.unlinkSync(filePath);
}
}
}
private async getPool(): Promise<Pool> { private async getPool(): Promise<Pool> {
if (this.pool === null) { if (this.pool === null) {
this.pool = createPool(this.poolConfig); this.pool = createPool(this.poolConfig);

View File

@ -91,11 +91,18 @@ class Server {
async startServer(worker = false): Promise<void> { async startServer(worker = false): Promise<void> {
logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`); logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`);
// Register cleanup listeners for exit events
['exit', 'SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'unhandledRejection'].forEach(event => {
process.on(event, () => { this.onExit(event); });
});
if (config.MEMPOOL.BACKEND === 'esplora') { if (config.MEMPOOL.BACKEND === 'esplora') {
bitcoinApi.startHealthChecks(); bitcoinApi.startHealthChecks();
} }
if (config.DATABASE.ENABLED) { if (config.DATABASE.ENABLED) {
DB.getPidLock();
await DB.checkDbConnection(); await DB.checkDbConnection();
try { try {
if (process.env.npm_config_reindex_blocks === 'true') { // Re-index requests if (process.env.npm_config_reindex_blocks === 'true') { // Re-index requests
@ -306,6 +313,15 @@ class Server {
this.lastHeapLogTime = now; this.lastHeapLogTime = now;
} }
} }
onExit(exitEvent): void {
if (config.DATABASE.ENABLED) {
DB.releasePidLock();
}
process.exit(0);
}
} }
((): Server => new Server())(); ((): Server => new Server())();

View File

@ -300,6 +300,7 @@ export interface Statistic {
total_fee: number; total_fee: number;
mempool_byte_weight: number; mempool_byte_weight: number;
fee_data: string; fee_data: string;
min_fee: number;
vsize_1: number; vsize_1: number;
vsize_2: number; vsize_2: number;
@ -346,6 +347,7 @@ export interface OptimizedStatistic {
vbytes_per_second: number; vbytes_per_second: number;
total_fee: number; total_fee: number;
mempool_byte_weight: number; mempool_byte_weight: number;
min_fee: number;
vsizes: number[]; vsizes: number[];
} }

3
contributors/TKone7.txt Normal file
View File

@ -0,0 +1,3 @@
I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of October 23, 2023.
Signed: TKone7

View File

@ -0,0 +1,3 @@
I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of October 23, 2023.
Signed: fanquake

3
contributors/fubz.txt Normal file
View File

@ -0,0 +1,3 @@
I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022.
Signed: fubz

View File

@ -1,4 +1,4 @@
FROM node:16.16.0-buster-slim AS builder FROM node:20.8.0-buster-slim AS builder
ARG commitHash ARG commitHash
ENV MEMPOOL_COMMIT_HASH=${commitHash} ENV MEMPOOL_COMMIT_HASH=${commitHash}
@ -17,7 +17,7 @@ ENV PATH="/root/.cargo/bin:$PATH"
RUN npm install --omit=dev --omit=optional RUN npm install --omit=dev --omit=optional
RUN npm run package RUN npm run package
FROM node:16.16.0-buster-slim FROM node:20.8.0-buster-slim
WORKDIR /backend WORKDIR /backend

View File

@ -52,6 +52,8 @@
"REST_API_URL": "__ESPLORA_REST_API_URL__", "REST_API_URL": "__ESPLORA_REST_API_URL__",
"UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__", "UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__",
"RETRY_UNIX_SOCKET_AFTER": __ESPLORA_RETRY_UNIX_SOCKET_AFTER__, "RETRY_UNIX_SOCKET_AFTER": __ESPLORA_RETRY_UNIX_SOCKET_AFTER__,
"REQUEST_TIMEOUT": __ESPLORA_REQUEST_TIMEOUT__,
"FALLBACK_TIMEOUT": __ESPLORA_FALLBACK_TIMEOUT__,
"FALLBACK": __ESPLORA_FALLBACK__ "FALLBACK": __ESPLORA_FALLBACK__
}, },
"SECOND_CORE_RPC": { "SECOND_CORE_RPC": {
@ -69,7 +71,8 @@
"DATABASE": "__DATABASE_DATABASE__", "DATABASE": "__DATABASE_DATABASE__",
"USERNAME": "__DATABASE_USERNAME__", "USERNAME": "__DATABASE_USERNAME__",
"PASSWORD": "__DATABASE_PASSWORD__", "PASSWORD": "__DATABASE_PASSWORD__",
"TIMEOUT": __DATABASE_TIMEOUT__ "TIMEOUT": __DATABASE_TIMEOUT__,
"PID_DIR": "__DATABASE_PID_DIR__"
}, },
"SYSLOG": { "SYSLOG": {
"ENABLED": __SYSLOG_ENABLED__, "ENABLED": __SYSLOG_ENABLED__,

View File

@ -53,6 +53,8 @@ __ELECTRUM_TLS_ENABLED__=${ELECTRUM_TLS_ENABLED:=false}
__ESPLORA_REST_API_URL__=${ESPLORA_REST_API_URL:=http://127.0.0.1:3000} __ESPLORA_REST_API_URL__=${ESPLORA_REST_API_URL:=http://127.0.0.1:3000}
__ESPLORA_UNIX_SOCKET_PATH__=${ESPLORA_UNIX_SOCKET_PATH:="null"} __ESPLORA_UNIX_SOCKET_PATH__=${ESPLORA_UNIX_SOCKET_PATH:="null"}
__ESPLORA_RETRY_UNIX_SOCKET_AFTER__=${ESPLORA_RETRY_UNIX_SOCKET_AFTER:=30000} __ESPLORA_RETRY_UNIX_SOCKET_AFTER__=${ESPLORA_RETRY_UNIX_SOCKET_AFTER:=30000}
__ESPLORA_REQUEST_TIMEOUT__=${ESPLORA_REQUEST_TIMEOUT:=5000}
__ESPLORA_FALLBACK_TIMEOUT__=${ESPLORA_FALLBACK_TIMEOUT:=5000}
__ESPLORA_FALLBACK__=${ESPLORA_FALLBACK:=[]} __ESPLORA_FALLBACK__=${ESPLORA_FALLBACK:=[]}
# SECOND_CORE_RPC # SECOND_CORE_RPC
@ -71,6 +73,7 @@ __DATABASE_DATABASE__=${DATABASE_DATABASE:=mempool}
__DATABASE_USERNAME__=${DATABASE_USERNAME:=mempool} __DATABASE_USERNAME__=${DATABASE_USERNAME:=mempool}
__DATABASE_PASSWORD__=${DATABASE_PASSWORD:=mempool} __DATABASE_PASSWORD__=${DATABASE_PASSWORD:=mempool}
__DATABASE_TIMEOUT__=${DATABASE_TIMEOUT:=180000} __DATABASE_TIMEOUT__=${DATABASE_TIMEOUT:=180000}
__DATABASE_PID_DIR__=${DATABASE_PID_DIR:=""}
# SYSLOG # SYSLOG
__SYSLOG_ENABLED__=${SYSLOG_ENABLED:=false} __SYSLOG_ENABLED__=${SYSLOG_ENABLED:=false}
@ -139,7 +142,7 @@ __MEMPOOL_SERVICES_API__=${MEMPOOL_SERVICES_API:=""}
__MEMPOOL_SERVICES_ACCELERATIONS__=${MEMPOOL_SERVICES_ACCELERATIONS:=false} __MEMPOOL_SERVICES_ACCELERATIONS__=${MEMPOOL_SERVICES_ACCELERATIONS:=false}
# REDIS # REDIS
__REDIS_ENABLED__=${REDIS_ENABLED:=true} __REDIS_ENABLED__=${REDIS_ENABLED:=false}
__REDIS_UNIX_SOCKET_PATH__=${REDIS_UNIX_SOCKET_PATH:=true} __REDIS_UNIX_SOCKET_PATH__=${REDIS_UNIX_SOCKET_PATH:=true}
mkdir -p "${__MEMPOOL_CACHE_DIR__}" mkdir -p "${__MEMPOOL_CACHE_DIR__}"
@ -193,6 +196,8 @@ sed -i "s!__ELECTRUM_TLS_ENABLED__!${__ELECTRUM_TLS_ENABLED__}!g" mempool-config
sed -i "s!__ESPLORA_REST_API_URL__!${__ESPLORA_REST_API_URL__}!g" mempool-config.json sed -i "s!__ESPLORA_REST_API_URL__!${__ESPLORA_REST_API_URL__}!g" mempool-config.json
sed -i "s!__ESPLORA_UNIX_SOCKET_PATH__!${__ESPLORA_UNIX_SOCKET_PATH__}!g" mempool-config.json sed -i "s!__ESPLORA_UNIX_SOCKET_PATH__!${__ESPLORA_UNIX_SOCKET_PATH__}!g" mempool-config.json
sed -i "s!__ESPLORA_RETRY_UNIX_SOCKET_AFTER__!${__ESPLORA_RETRY_UNIX_SOCKET_AFTER__}!g" mempool-config.json sed -i "s!__ESPLORA_RETRY_UNIX_SOCKET_AFTER__!${__ESPLORA_RETRY_UNIX_SOCKET_AFTER__}!g" mempool-config.json
sed -i "s!__ESPLORA_REQUEST_TIMEOUT__!${__ESPLORA_REQUEST_TIMEOUT__}!g" mempool-config.json
sed -i "s!__ESPLORA_FALLBACK_TIMEOUT__!${__ESPLORA_FALLBACK_TIMEOUT__}!g" mempool-config.json
sed -i "s!__ESPLORA_FALLBACK__!${__ESPLORA_FALLBACK__}!g" mempool-config.json sed -i "s!__ESPLORA_FALLBACK__!${__ESPLORA_FALLBACK__}!g" mempool-config.json
sed -i "s!__SECOND_CORE_RPC_HOST__!${__SECOND_CORE_RPC_HOST__}!g" mempool-config.json sed -i "s!__SECOND_CORE_RPC_HOST__!${__SECOND_CORE_RPC_HOST__}!g" mempool-config.json
@ -209,6 +214,7 @@ sed -i "s!__DATABASE_DATABASE__!${__DATABASE_DATABASE__}!g" mempool-config.json
sed -i "s!__DATABASE_USERNAME__!${__DATABASE_USERNAME__}!g" mempool-config.json sed -i "s!__DATABASE_USERNAME__!${__DATABASE_USERNAME__}!g" mempool-config.json
sed -i "s!__DATABASE_PASSWORD__!${__DATABASE_PASSWORD__}!g" mempool-config.json sed -i "s!__DATABASE_PASSWORD__!${__DATABASE_PASSWORD__}!g" mempool-config.json
sed -i "s!__DATABASE_TIMEOUT__!${__DATABASE_TIMEOUT__}!g" mempool-config.json sed -i "s!__DATABASE_TIMEOUT__!${__DATABASE_TIMEOUT__}!g" mempool-config.json
sed -i "s!__DATABASE_PID_DIR__!${__DATABASE_PID_DIR__}!g" mempool-config.json
sed -i "s!__SYSLOG_ENABLED__!${__SYSLOG_ENABLED__}!g" mempool-config.json sed -i "s!__SYSLOG_ENABLED__!${__SYSLOG_ENABLED__}!g" mempool-config.json
sed -i "s!__SYSLOG_HOST__!${__SYSLOG_HOST__}!g" mempool-config.json sed -i "s!__SYSLOG_HOST__!${__SYSLOG_HOST__}!g" mempool-config.json

View File

@ -38,7 +38,7 @@ services:
MYSQL_USER: "mempool" MYSQL_USER: "mempool"
MYSQL_PASSWORD: "mempool" MYSQL_PASSWORD: "mempool"
MYSQL_ROOT_PASSWORD: "admin" MYSQL_ROOT_PASSWORD: "admin"
image: mariadb:10.5.8 image: mariadb:10.5.21
user: "1000:1000" user: "1000:1000"
restart: on-failure restart: on-failure
stop_grace_period: 1m stop_grace_period: 1m

View File

@ -1,32 +0,0 @@
FROM ubuntu:18.04
MAINTAINER mempool.space developers
EXPOSE 50002
# runs as UID 1000 GID 1000 inside the container
ENV VERSION 4.0.9
RUN set -x \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends gpg gpg-agent dirmngr \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends wget xpra python3-pyqt5 python3-wheel python3-pip python3-setuptools libsecp256k1-0 libsecp256k1-dev python3-numpy python3-dev build-essential \
&& wget -O /tmp/Electrum-${VERSION}.tar.gz https://download.electrum.org/${VERSION}/Electrum-${VERSION}.tar.gz \
&& wget -O /tmp/Electrum-${VERSION}.tar.gz.asc https://download.electrum.org/${VERSION}/Electrum-${VERSION}.tar.gz.asc \
&& gpg --keyserver keys.gnupg.net --recv-keys 6694D8DE7BE8EE5631BED9502BD5824B7F9470E6 \
&& gpg --verify /tmp/Electrum-${VERSION}.tar.gz.asc /tmp/Electrum-${VERSION}.tar.gz \
&& pip3 install /tmp/Electrum-${VERSION}.tar.gz \
&& test -f /usr/local/bin/electrum \
&& rm -vrf /tmp/Electrum-${VERSION}.tar.gz /tmp/Electrum-${VERSION}.tar.gz.asc ${HOME}/.gnupg \
&& apt-get purge --autoremove -y python3-wheel python3-pip python3-setuptools python3-dev build-essential libsecp256k1-dev curl gpg gpg-agent dirmngr \
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
&& useradd -d /home/mempool -m mempool \
&& mkdir /electrum \
&& ln -s /electrum /home/mempool/.electrum \
&& chown mempool:mempool /electrum
USER mempool
ENV HOME /home/mempool
WORKDIR /home/mempool
VOLUME /electrum
CMD ["/usr/bin/xpra", "start", ":100", "--start-child=/usr/local/bin/electrum", "--bind-tcp=0.0.0.0:50002","--daemon=yes", "--notifications=no", "--mdns=no", "--pulseaudio=no", "--html=off", "--speaker=disabled", "--microphone=disabled", "--webcam=no", "--printing=no", "--dbus-launch=", "--exit-with-children"]
ENTRYPOINT ["electrum"]

View File

@ -1,4 +1,4 @@
FROM node:16.16.0-buster-slim AS builder FROM node:20.8.0-buster-slim AS builder
ARG commitHash ARG commitHash
ENV DOCKER_COMMIT_HASH=${commitHash} ENV DOCKER_COMMIT_HASH=${commitHash}
@ -13,7 +13,7 @@ RUN npm install --omit=dev --omit=optional
RUN npm run build RUN npm run build
FROM nginx:1.17.8-alpine FROM nginx:1.24.0-alpine
WORKDIR /patch WORKDIR /patch

View File

@ -39,6 +39,7 @@ __AUDIT__=${AUDIT:=false}
__MAINNET_BLOCK_AUDIT_START_HEIGHT__=${MAINNET_BLOCK_AUDIT_START_HEIGHT:=0} __MAINNET_BLOCK_AUDIT_START_HEIGHT__=${MAINNET_BLOCK_AUDIT_START_HEIGHT:=0}
__TESTNET_BLOCK_AUDIT_START_HEIGHT__=${TESTNET_BLOCK_AUDIT_START_HEIGHT:=0} __TESTNET_BLOCK_AUDIT_START_HEIGHT__=${TESTNET_BLOCK_AUDIT_START_HEIGHT:=0}
__SIGNET_BLOCK_AUDIT_START_HEIGHT__=${SIGNET_BLOCK_AUDIT_START_HEIGHT:=0} __SIGNET_BLOCK_AUDIT_START_HEIGHT__=${SIGNET_BLOCK_AUDIT_START_HEIGHT:=0}
__ACCELERATOR__=${ACCELERATOR:=false}
__HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true} __HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true}
# Export as environment variables to be used by envsubst # Export as environment variables to be used by envsubst
@ -65,6 +66,7 @@ export __AUDIT__
export __MAINNET_BLOCK_AUDIT_START_HEIGHT__ export __MAINNET_BLOCK_AUDIT_START_HEIGHT__
export __TESTNET_BLOCK_AUDIT_START_HEIGHT__ export __TESTNET_BLOCK_AUDIT_START_HEIGHT__
export __SIGNET_BLOCK_AUDIT_START_HEIGHT__ export __SIGNET_BLOCK_AUDIT_START_HEIGHT__
export __ACCELERATOR__
export __HISTORICAL_PRICE__ export __HISTORICAL_PRICE__
folder=$(find /var/www/mempool -name "config.js" | xargs dirname) folder=$(find /var/www/mempool -name "config.js" | xargs dirname)

View File

@ -31,9 +31,9 @@
"bootstrap": "~4.6.2", "bootstrap": "~4.6.2",
"browserify": "^17.0.0", "browserify": "^17.0.0",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"cypress": "^13.5.0",
"domino": "^2.1.6", "domino": "^2.1.6",
"echarts": "~5.4.3", "echarts": "~5.4.3",
"echarts-gl": "^2.0.9",
"lightweight-charts": "~3.8.0", "lightweight-charts": "~3.8.0",
"ngx-echarts": "~16.0.0", "ngx-echarts": "~16.0.0",
"ngx-infinite-scroll": "^16.0.0", "ngx-infinite-scroll": "^16.0.0",
@ -59,7 +59,7 @@
"optionalDependencies": { "optionalDependencies": {
"@cypress/schematic": "^2.5.0", "@cypress/schematic": "^2.5.0",
"@types/cypress": "^1.1.3", "@types/cypress": "^1.1.3",
"cypress": "^13.2.0", "cypress": "^13.5.0",
"cypress-fail-on-console-error": "~5.0.0", "cypress-fail-on-console-error": "~5.0.0",
"cypress-wait-until": "^2.0.1", "cypress-wait-until": "^2.0.1",
"mock-socket": "~9.2.1", "mock-socket": "~9.2.1",
@ -1250,11 +1250,12 @@
"integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg=="
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.22.5", "version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dependencies": { "dependencies": {
"@babel/highlight": "^7.22.5" "@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -1464,20 +1465,33 @@
} }
}, },
"node_modules/@babel/helper-environment-visitor": { "node_modules/@babel/helper-environment-visitor": {
"version": "7.22.5", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-function-name": { "node_modules/@babel/helper-function-name": {
"version": "7.22.5", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"dependencies": { "dependencies": {
"@babel/template": "^7.22.5", "@babel/template": "^7.22.15",
"@babel/types": "^7.22.5" "@babel/types": "^7.23.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name/node_modules/@babel/template": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"dependencies": {
"@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.22.15",
"@babel/types": "^7.22.15"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -1627,9 +1641,9 @@
} }
}, },
"node_modules/@babel/helper-validator-identifier": { "node_modules/@babel/helper-validator-identifier": {
"version": "7.22.5", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@ -1669,12 +1683,12 @@
} }
}, },
"node_modules/@babel/highlight": { "node_modules/@babel/highlight": {
"version": "7.22.5", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.0.0", "chalk": "^2.4.2",
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
}, },
"engines": { "engines": {
@ -1682,9 +1696,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.22.7", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
}, },
@ -2879,18 +2893,18 @@
} }
}, },
"node_modules/@babel/traverse": { "node_modules/@babel/traverse": {
"version": "7.22.8", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.22.5", "@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.22.7", "@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.22.5", "@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6", "@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.7", "@babel/parser": "^7.23.0",
"@babel/types": "^7.22.5", "@babel/types": "^7.23.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"globals": "^11.1.0" "globals": "^11.1.0"
}, },
@ -2898,13 +2912,36 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/traverse/node_modules/@babel/generator": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dependencies": {
"@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse/node_modules/@jridgewell/trace-mapping": {
"version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.22.10", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.22.5", "@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
}, },
"engines": { "engines": {
@ -5574,9 +5611,9 @@
"optional": true "optional": true
}, },
"node_modules/bn.js": { "node_modules/bn.js": {
"version": "5.2.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
"integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.1", "version": "1.20.1",
@ -5871,25 +5908,28 @@
} }
}, },
"node_modules/browserify-sign": { "node_modules/browserify-sign": {
"version": "4.2.1", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz",
"integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==",
"dependencies": { "dependencies": {
"bn.js": "^5.1.1", "bn.js": "^5.2.1",
"browserify-rsa": "^4.0.1", "browserify-rsa": "^4.1.0",
"create-hash": "^1.2.0", "create-hash": "^1.2.0",
"create-hmac": "^1.1.7", "create-hmac": "^1.1.7",
"elliptic": "^6.5.3", "elliptic": "^6.5.4",
"inherits": "^2.0.4", "inherits": "^2.0.4",
"parse-asn1": "^5.1.5", "parse-asn1": "^5.1.6",
"readable-stream": "^3.6.0", "readable-stream": "^3.6.2",
"safe-buffer": "^5.2.0" "safe-buffer": "^5.2.1"
},
"engines": {
"node": ">= 4"
} }
}, },
"node_modules/browserify-sign/node_modules/readable-stream": { "node_modules/browserify-sign/node_modules/readable-stream": {
"version": "3.6.0", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": { "dependencies": {
"inherits": "^2.0.3", "inherits": "^2.0.3",
"string_decoder": "^1.1.1", "string_decoder": "^1.1.1",
@ -6389,11 +6429,6 @@
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
"node_modules/claygl": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
},
"node_modules/clean-stack": { "node_modules/clean-stack": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@ -7113,9 +7148,9 @@
"peer": true "peer": true
}, },
"node_modules/cypress": { "node_modules/cypress": {
"version": "13.2.0", "version": "13.5.0",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.2.0.tgz", "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.5.0.tgz",
"integrity": "sha512-AvDQxBydE771GTq0TR4ZUBvv9m9ffXuB/ueEtpDF/6gOcvFR96amgwSJP16Yhqw6VhmwqspT5nAGzoxxB+D89g==", "integrity": "sha512-oh6U7h9w8wwHfzNDJQ6wVcAeXu31DlIYlNOBvfd6U4CcB8oe4akawQmH+QJVOMZlM42eBoCne015+svVqdwdRQ==",
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
"dependencies": { "dependencies": {
@ -7785,18 +7820,6 @@
"zrender": "5.4.4" "zrender": "5.4.4"
} }
}, },
"node_modules/echarts-gl": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
"dependencies": {
"claygl": "^1.2.1",
"zrender": "^5.1.1"
},
"peerDependencies": {
"echarts": "^5.1.2"
}
},
"node_modules/echarts/node_modules/tslib": { "node_modules/echarts/node_modules/tslib": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
@ -17768,11 +17791,12 @@
"integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg=="
}, },
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.22.5", "version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"requires": { "requires": {
"@babel/highlight": "^7.22.5" "@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
} }
}, },
"@babel/compat-data": { "@babel/compat-data": {
@ -17937,17 +17961,29 @@
} }
}, },
"@babel/helper-environment-visitor": { "@babel/helper-environment-visitor": {
"version": "7.22.5", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==" "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA=="
}, },
"@babel/helper-function-name": { "@babel/helper-function-name": {
"version": "7.22.5", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"requires": { "requires": {
"@babel/template": "^7.22.5", "@babel/template": "^7.22.15",
"@babel/types": "^7.22.5" "@babel/types": "^7.23.0"
},
"dependencies": {
"@babel/template": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"requires": {
"@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.22.15",
"@babel/types": "^7.22.15"
}
}
} }
}, },
"@babel/helper-hoist-variables": { "@babel/helper-hoist-variables": {
@ -18049,9 +18085,9 @@
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw=="
}, },
"@babel/helper-validator-identifier": { "@babel/helper-validator-identifier": {
"version": "7.22.5", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
}, },
"@babel/helper-validator-option": { "@babel/helper-validator-option": {
"version": "7.22.5", "version": "7.22.5",
@ -18079,19 +18115,19 @@
} }
}, },
"@babel/highlight": { "@babel/highlight": {
"version": "7.22.5", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"requires": { "requires": {
"@babel/helper-validator-identifier": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.0.0", "chalk": "^2.4.2",
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.22.7", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==" "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw=="
}, },
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.22.5", "version": "7.22.5",
@ -18868,29 +18904,51 @@
} }
}, },
"@babel/traverse": { "@babel/traverse": {
"version": "7.22.8", "version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"requires": { "requires": {
"@babel/code-frame": "^7.22.5", "@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.22.7", "@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.22.5", "@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6", "@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.7", "@babel/parser": "^7.23.0",
"@babel/types": "^7.22.5", "@babel/types": "^7.23.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"globals": "^11.1.0" "globals": "^11.1.0"
},
"dependencies": {
"@babel/generator": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"requires": {
"@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
}
},
"@jridgewell/trace-mapping": {
"version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
"requires": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
}
} }
}, },
"@babel/types": { "@babel/types": {
"version": "7.22.10", "version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"requires": { "requires": {
"@babel/helper-string-parser": "^7.22.5", "@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
}, },
@ -20846,9 +20904,9 @@
"optional": true "optional": true
}, },
"bn.js": { "bn.js": {
"version": "5.2.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
"integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
}, },
"body-parser": { "body-parser": {
"version": "1.20.1", "version": "1.20.1",
@ -21192,25 +21250,25 @@
} }
}, },
"browserify-sign": { "browserify-sign": {
"version": "4.2.1", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz",
"integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==",
"requires": { "requires": {
"bn.js": "^5.1.1", "bn.js": "^5.2.1",
"browserify-rsa": "^4.0.1", "browserify-rsa": "^4.1.0",
"create-hash": "^1.2.0", "create-hash": "^1.2.0",
"create-hmac": "^1.1.7", "create-hmac": "^1.1.7",
"elliptic": "^6.5.3", "elliptic": "^6.5.4",
"inherits": "^2.0.4", "inherits": "^2.0.4",
"parse-asn1": "^5.1.5", "parse-asn1": "^5.1.6",
"readable-stream": "^3.6.0", "readable-stream": "^3.6.2",
"safe-buffer": "^5.2.0" "safe-buffer": "^5.2.1"
}, },
"dependencies": { "dependencies": {
"readable-stream": { "readable-stream": {
"version": "3.6.0", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"requires": { "requires": {
"inherits": "^2.0.3", "inherits": "^2.0.3",
"string_decoder": "^1.1.1", "string_decoder": "^1.1.1",
@ -21475,11 +21533,6 @@
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
"claygl": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
},
"clean-stack": { "clean-stack": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@ -22043,9 +22096,9 @@
"peer": true "peer": true
}, },
"cypress": { "cypress": {
"version": "13.2.0", "version": "13.5.0",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.2.0.tgz", "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.5.0.tgz",
"integrity": "sha512-AvDQxBydE771GTq0TR4ZUBvv9m9ffXuB/ueEtpDF/6gOcvFR96amgwSJP16Yhqw6VhmwqspT5nAGzoxxB+D89g==", "integrity": "sha512-oh6U7h9w8wwHfzNDJQ6wVcAeXu31DlIYlNOBvfd6U4CcB8oe4akawQmH+QJVOMZlM42eBoCne015+svVqdwdRQ==",
"optional": true, "optional": true,
"requires": { "requires": {
"@cypress/request": "^3.0.0", "@cypress/request": "^3.0.0",
@ -22584,15 +22637,6 @@
} }
} }
}, },
"echarts-gl": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
"requires": {
"claygl": "^1.2.1",
"zrender": "^5.1.1"
}
},
"ee-first": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",

View File

@ -85,7 +85,6 @@
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"domino": "^2.1.6", "domino": "^2.1.6",
"echarts": "~5.4.3", "echarts": "~5.4.3",
"echarts-gl": "^2.0.9",
"lightweight-charts": "~3.8.0", "lightweight-charts": "~3.8.0",
"ngx-echarts": "~16.0.0", "ngx-echarts": "~16.0.0",
"ngx-infinite-scroll": "^16.0.0", "ngx-infinite-scroll": "^16.0.0",
@ -111,7 +110,7 @@
"optionalDependencies": { "optionalDependencies": {
"@cypress/schematic": "^2.5.0", "@cypress/schematic": "^2.5.0",
"@types/cypress": "^1.1.3", "@types/cypress": "^1.1.3",
"cypress": "^13.2.0", "cypress": "^13.5.0",
"cypress-fail-on-console-error": "~5.0.0", "cypress-fail-on-console-error": "~5.0.0",
"cypress-wait-until": "^2.0.1", "cypress-wait-until": "^2.0.1",
"mock-socket": "~9.2.1", "mock-socket": "~9.2.1",

View File

@ -1,28 +1,10 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
import { AppPreloadingStrategy } from './app.preloading-strategy' import { AppPreloadingStrategy } from './app.preloading-strategy'
import { StartComponent } from './components/start/start.component'; import { BlockViewComponent } from './components/block-view/block-view.component';
import { TransactionComponent } from './components/transaction/transaction.component'; import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component';
import { BlockComponent } from './components/block/block.component';
import { ClockComponent } from './components/clock/clock.component'; import { ClockComponent } from './components/clock/clock.component';
import { AddressComponent } from './components/address/address.component';
import { MasterPageComponent } from './components/master-page/master-page.component';
import { AboutComponent } from './components/about/about.component';
import { StatusViewComponent } from './components/status-view/status-view.component'; import { StatusViewComponent } from './components/status-view/status-view.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 { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
import { BlocksList } from './components/blocks-list/blocks-list.component';
import { RbfList } from './components/rbf-list/rbf-list.component';
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component';
import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component';
import { AssetsComponent } from './components/assets/assets.component';
import { AssetComponent } from './components/asset/asset.component';
import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component';
import { CalculatorComponent } from './components/calculator/calculator.component';
const browserWindow = window || {}; const browserWindow = window || {};
// @ts-ignore // @ts-ignore
@ -35,95 +17,13 @@ let routes: Routes = [
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule), loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true }, data: { preload: true },
}, },
{ {
path: '', path: '',
component: MasterPageComponent, loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule),
children: [ data: { preload: true },
{
path: 'mining/blocks',
redirectTo: 'blocks',
pathMatch: 'full'
},
{
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'blocks',
component: BlocksList,
},
{
path: 'rbf',
component: RbfList,
},
{
path: 'terms-of-service',
component: TermsOfServiceComponent
},
{
path: 'privacy-policy',
component: PrivacyPolicyComponent
},
{
path: 'trademark-policy',
component: TrademarkPolicyComponent
},
{
path: 'address/:id',
children: [],
component: AddressComponent,
data: {
ogImage: true,
networkSpecific: true,
}
},
{
path: 'tx',
component: StartComponent,
data: { networkSpecific: true },
children: [
{
path: ':id',
component: TransactionComponent
},
],
},
{
path: 'block',
component: StartComponent,
data: { networkSpecific: true },
children: [
{
path: ':id',
component: BlockComponent,
data: {
ogImage: true
}
},
],
},
{
path: 'docs',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule),
data: { preload: true },
},
{
path: 'api',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'lightning',
loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule),
data: { preload: browserWindowEnv && browserWindowEnv.LIGHTNING === true, networks: ['bitcoin'] },
},
],
}, },
{ {
path: 'status', path: 'status',
@ -132,7 +32,8 @@ let routes: Routes = [
}, },
{ {
path: '', path: '',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true },
}, },
{ {
path: '**', path: '**',
@ -151,88 +52,13 @@ let routes: Routes = [
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true },
}, },
{ {
path: '', path: '',
component: MasterPageComponent, loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule),
children: [ data: { preload: true },
{
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'blocks',
component: BlocksList,
},
{
path: 'rbf',
component: RbfList,
},
{
path: 'terms-of-service',
component: TermsOfServiceComponent
},
{
path: 'privacy-policy',
component: PrivacyPolicyComponent
},
{
path: 'trademark-policy',
component: TrademarkPolicyComponent
},
{
path: 'address/:id',
children: [],
component: AddressComponent,
data: {
ogImage: true,
networkSpecific: true,
}
},
{
path: 'tx',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: TransactionComponent
},
],
},
{
path: 'block',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: BlockComponent,
data: {
ogImage: true
}
},
],
},
{
path: 'docs',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'api',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'lightning',
data: { networks: ['bitcoin'] },
loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule)
},
],
}, },
{ {
path: 'status', path: 'status',
@ -241,7 +67,8 @@ let routes: Routes = [
}, },
{ {
path: '', path: '',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true },
}, },
{ {
path: '**', path: '**',
@ -252,97 +79,13 @@ let routes: Routes = [
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true },
}, },
{ {
path: '', path: '',
component: MasterPageComponent, loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule),
children: [ data: { preload: true },
{
path: 'mining/blocks',
redirectTo: 'blocks',
pathMatch: 'full'
},
{
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'blocks',
component: BlocksList,
},
{
path: 'rbf',
component: RbfList,
},
{
path: 'tools/calculator',
component: CalculatorComponent
},
{
path: 'terms-of-service',
component: TermsOfServiceComponent
},
{
path: 'privacy-policy',
component: PrivacyPolicyComponent
},
{
path: 'trademark-policy',
component: TrademarkPolicyComponent
},
{
path: 'address/:id',
children: [],
component: AddressComponent,
data: {
ogImage: true,
networkSpecific: true,
}
},
{
path: 'tx',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: TransactionComponent
},
],
},
{
path: 'block',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: BlockComponent,
data: {
ogImage: true
}
},
],
},
{
path: 'docs',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'api',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'lightning',
data: { networks: ['bitcoin'] },
loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule)
},
],
}, },
{ {
path: 'preview', path: 'preview',
@ -373,6 +116,14 @@ let routes: Routes = [
path: 'clock/:mode/:index', path: 'clock/:mode/:index',
component: ClockComponent, component: ClockComponent,
}, },
{
path: 'view/block/:id',
component: BlockViewComponent,
},
{
path: 'view/mempool-block/:index',
component: MempoolBlockViewComponent,
},
{ {
path: 'status', path: 'status',
data: { networks: ['bitcoin', 'liquid'] }, data: { networks: ['bitcoin', 'liquid'] },
@ -380,7 +131,8 @@ let routes: Routes = [
}, },
{ {
path: '', path: '',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule),
data: { preload: true },
}, },
{ {
path: '**', path: '**',
@ -391,7 +143,6 @@ let routes: Routes = [
if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') { if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') {
routes = [{ routes = [{
path: '', path: '',
component: BisqMasterPageComponent,
loadChildren: () => import('./bisq/bisq.module').then(m => m.BisqModule) loadChildren: () => import('./bisq/bisq.module').then(m => m.BisqModule)
}]; }];
} }
@ -404,105 +155,13 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule),
data: { preload: true },
}, },
{ {
path: '', path: '',
component: LiquidMasterPageComponent, loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule),
children: [ data: { preload: true },
{
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'blocks',
component: BlocksList,
},
{
path: 'terms-of-service',
component: TermsOfServiceComponent
},
{
path: 'privacy-policy',
component: PrivacyPolicyComponent
},
{
path: 'trademark-policy',
component: TrademarkPolicyComponent
},
{
path: 'address/:id',
children: [],
component: AddressComponent,
data: {
ogImage: true,
networkSpecific: true,
}
},
{
path: 'tx',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: TransactionComponent
},
],
},
{
path: 'block',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: BlockComponent,
data: {
ogImage: true
}
},
],
},
{
path: 'assets',
data: { networks: ['liquid'] },
component: AssetsNavComponent,
children: [
{
path: 'all',
data: { networks: ['liquid'] },
component: AssetsComponent,
},
{
path: 'asset/:id',
data: { networkSpecific: true },
component: AssetComponent
},
{
path: 'group/:id',
data: { networkSpecific: true },
component: AssetGroupComponent
},
{
path: '**',
redirectTo: 'all'
}
]
},
{
path: 'docs',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'api',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
],
}, },
{ {
path: 'status', path: 'status',
@ -511,7 +170,8 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
}, },
{ {
path: '', path: '',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule),
data: { preload: true },
}, },
{ {
path: '**', path: '**',
@ -522,110 +182,13 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule),
data: { preload: true },
}, },
{ {
path: '', path: '',
component: LiquidMasterPageComponent, loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule),
children: [ data: { preload: true },
{
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
component: AboutComponent,
},
{
path: 'blocks',
component: BlocksList,
},
{
path: 'terms-of-service',
component: TermsOfServiceComponent
},
{
path: 'privacy-policy',
component: PrivacyPolicyComponent
},
{
path: 'trademark-policy',
component: TrademarkPolicyComponent
},
{
path: 'address/:id',
children: [],
component: AddressComponent,
data: {
ogImage: true,
networkSpecific: true,
}
},
{
path: 'tx',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: TransactionComponent
},
],
},
{
path: 'block',
data: { networkSpecific: true },
component: StartComponent,
children: [
{
path: ':id',
component: BlockComponent,
data: {
ogImage: true
}
},
],
},
{
path: 'assets',
data: { networks: ['liquid'] },
component: AssetsNavComponent,
children: [
{
path: 'featured',
data: { networkSpecific: true },
component: AssetsFeaturedComponent,
},
{
path: 'all',
data: { networks: ['liquid'] },
component: AssetsComponent,
},
{
path: 'asset/:id',
data: { networkSpecific: true },
component: AssetComponent
},
{
path: 'group/:id',
data: { networkSpecific: true },
component: AssetGroupComponent
},
{
path: '**',
redirectTo: 'featured'
}
]
},
{
path: 'docs',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'api',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
],
}, },
{ {
path: 'preview', path: 'preview',
@ -647,7 +210,8 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
}, },
{ {
path: '', path: '',
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule),
data: { preload: true },
}, },
{ {
path: '**', path: '**',

View File

@ -27,9 +27,11 @@ import { AutofocusDirective } from '../components/ngx-bootstrap-multiselect/auto
import { MultiSelectSearchFilter } from '../components/ngx-bootstrap-multiselect/search-filter.pipe'; import { MultiSelectSearchFilter } from '../components/ngx-bootstrap-multiselect/search-filter.pipe';
import { OffClickDirective } from '../components/ngx-bootstrap-multiselect/off-click.directive'; import { OffClickDirective } from '../components/ngx-bootstrap-multiselect/off-click.directive';
import { NgxDropdownMultiselectComponent } from '../components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component'; import { NgxDropdownMultiselectComponent } from '../components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component';
import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
BisqMasterPageComponent,
BisqTransactionsComponent, BisqTransactionsComponent,
BisqTransactionComponent, BisqTransactionComponent,
BisqBlockComponent, BisqBlockComponent,

View File

@ -1,6 +1,6 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { AboutComponent } from '../components/about/about.component'; import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component';
import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component';
import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component';
import { BisqBlockComponent } from './bisq-block/bisq-block.component'; import { BisqBlockComponent } from './bisq-block/bisq-block.component';
@ -10,78 +10,83 @@ import { BisqStatsComponent } from './bisq-stats/bisq-stats.component';
import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component';
import { BisqMarketComponent } from './bisq-market/bisq-market.component'; import { BisqMarketComponent } from './bisq-market/bisq-market.component';
import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component';
import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component';
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
component: BisqMainDashboardComponent, component: BisqMasterPageComponent,
}, children: [
{ {
path: 'markets', path: '',
data: { networks: ['bisq'] }, component: BisqMainDashboardComponent,
component: BisqDashboardComponent, },
}, {
{ path: 'markets',
path: 'transactions', data: { networks: ['bisq'] },
data: { networks: ['bisq'] }, component: BisqDashboardComponent,
component: BisqTransactionsComponent },
}, {
{ path: 'transactions',
path: 'market/:pair', data: { networks: ['bisq'] },
data: { networkSpecific: true }, component: BisqTransactionsComponent
component: BisqMarketComponent, },
}, {
{ path: 'market/:pair',
path: 'tx/push', data: { networkSpecific: true },
component: PushTransactionComponent, component: BisqMarketComponent,
}, },
{ {
path: 'tx/:id', path: 'tx/push',
data: { networkSpecific: true }, component: PushTransactionComponent,
component: BisqTransactionComponent },
}, {
{ path: 'tx/:id',
path: 'blocks', data: { networkSpecific: true },
children: [], component: BisqTransactionComponent
component: BisqBlocksComponent },
}, {
{ path: 'blocks',
path: 'block/:id', children: [],
data: { networkSpecific: true }, component: BisqBlocksComponent
component: BisqBlockComponent, },
}, {
{ path: 'block/:id',
path: 'address/:id', data: { networkSpecific: true },
data: { networkSpecific: true }, component: BisqBlockComponent,
component: BisqAddressComponent, },
}, {
{ path: 'address/:id',
path: 'stats', data: { networkSpecific: true },
data: { networks: ['bisq'] }, component: BisqAddressComponent,
component: BisqStatsComponent, },
}, {
{ path: 'stats',
path: 'about', data: { networks: ['bisq'] },
component: AboutComponent, component: BisqStatsComponent,
}, },
{ {
path: 'docs', path: 'about',
loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule),
}, },
{ {
path: 'api', path: 'docs',
loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule)
}, },
{ {
path: 'terms-of-service', path: 'api',
component: TermsOfServiceComponent loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule)
}, },
{ {
path: '**', path: 'terms-of-service',
redirectTo: '' loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule),
} },
{
path: '**',
redirectTo: ''
}
]
}
]; ];
@NgModule({ @NgModule({

View File

@ -0,0 +1,37 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { MasterPageComponent } from './components/master-page/master-page.component';
const routes: Routes = [
{
path: '',
component: MasterPageComponent,
loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule),
data: { preload: true },
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class BitcoinGraphsRoutingModule { }
@NgModule({
imports: [
CommonModule,
BitcoinGraphsRoutingModule,
],
})
export class BitcoinGraphsModule { }

View File

@ -0,0 +1,40 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { AboutComponent } from './about.component';
import { SharedModule } from '../../shared/shared.module';
const routes: Routes = [
{
path: '',
component: AboutComponent,
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class AboutRoutingModule { }
@NgModule({
imports: [
CommonModule,
AboutRoutingModule,
SharedModule,
],
declarations: [
AboutComponent,
]
})
export class AboutModule { }

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
import { Observable, Subscription, combineLatest } from 'rxjs'; import { Observable, Subscription, combineLatest } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
import { EChartsOption, graphic } from 'echarts'; import { echarts, EChartsOption } from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';
@ -123,11 +123,11 @@ export class BlockFeesGraphComponent implements OnInit {
this.chartOptions = { this.chartOptions = {
title: title, title: title,
color: [ color: [
new graphic.LinearGradient(0, 0, 0, 1, [ new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#FDD835' }, { offset: 0, color: '#FDD835' },
{ offset: 1, color: '#FB8C00' }, { offset: 1, color: '#FB8C00' },
]), ]),
new graphic.LinearGradient(0, 0, 0, 1, [ new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#C0CA33' }, { offset: 0, color: '#C0CA33' },
{ offset: 1, color: '#1B5E20' }, { offset: 1, color: '#1B5E20' },
]), ]),

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
import { EChartsOption, graphic } from 'echarts'; import { echarts, EChartsOption } from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';
@ -123,11 +123,11 @@ export class BlockRewardsGraphComponent implements OnInit {
title: title, title: title,
animation: false, animation: false,
color: [ color: [
new graphic.LinearGradient(0, 0, 0, 1, [ new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#FDD835' }, { offset: 0, color: '#FDD835' },
{ offset: 1, color: '#FB8C00' }, { offset: 1, color: '#FB8C00' },
]), ]),
new graphic.LinearGradient(0, 0, 0, 1, [ new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#C0CA33' }, { offset: 0, color: '#C0CA33' },
{ offset: 1, color: '#1B5E20' }, { offset: 1, color: '#1B5E20' },
]), ]),

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
import { EChartsOption} from 'echarts'; import { EChartsOption} from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';

View File

@ -0,0 +1,14 @@
<div class="block-wrapper">
<div class="block-container">
<app-block-overview-graph
#blockGraph
[isLoading]="false"
[resolution]="resolution"
[blockLimit]="stateService.blockVSize"
[orientation]="'top'"
[flip]="false"
[disableSpinner]="true"
(txClickEvent)="onTxClick($event)"
></app-block-overview-graph>
</div>
</div>

View File

@ -0,0 +1,22 @@
.block-wrapper {
width: 100vw;
height: 100vh;
background: #181b2d;
}
.block-container {
flex-grow: 0;
flex-shrink: 0;
width: 100vw;
max-width: 100vh;
height: 100vh;
padding: 0;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
* {
flex-grow: 1;
}
}

View File

@ -0,0 +1,180 @@
import { Component, OnInit, OnDestroy, ViewChild, HostListener } from '@angular/core';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { ElectrsApiService } from '../../services/electrs-api.service';
import { switchMap, tap, catchError, shareReplay, filter } from 'rxjs/operators';
import { of, Subscription } from 'rxjs';
import { StateService } from '../../services/state.service';
import { SeoService } from '../../services/seo.service';
import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface';
import { ApiService } from '../../services/api.service';
import { seoDescriptionNetwork } from '../../shared/common.utils';
import { BlockOverviewGraphComponent } from '../block-overview-graph/block-overview-graph.component';
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
function bestFitResolution(min, max, n): number {
const target = (min + max) / 2;
let bestScore = Infinity;
let best = null;
for (let i = min; i <= max; i++) {
const remainder = (n % i);
if (remainder < bestScore || (remainder === bestScore && (Math.abs(i - target) < Math.abs(best - target)))) {
bestScore = remainder;
best = i;
}
}
return best;
}
@Component({
selector: 'app-block-view',
templateUrl: './block-view.component.html',
styleUrls: ['./block-view.component.scss']
})
export class BlockViewComponent implements OnInit, OnDestroy {
network = '';
block: BlockExtended;
blockHeight: number;
blockHash: string;
rawId: string;
isLoadingBlock = true;
strippedTransactions: TransactionStripped[];
isLoadingOverview = true;
autofit: boolean = false;
resolution: number = 80;
overviewSubscription: Subscription;
networkChangedSubscription: Subscription;
queryParamsSubscription: Subscription;
@ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent;
constructor(
private route: ActivatedRoute,
private router: Router,
private electrsApiService: ElectrsApiService,
public stateService: StateService,
private seoService: SeoService,
private apiService: ApiService
) { }
ngOnInit(): void {
this.network = this.stateService.network;
this.queryParamsSubscription = this.route.queryParams.subscribe((params) => {
this.autofit = params.autofit === 'true';
if (this.autofit) {
this.onResize();
}
});
const block$ = this.route.paramMap.pipe(
switchMap((params: ParamMap) => {
this.rawId = params.get('id') || '';
const blockHash: string = params.get('id') || '';
this.block = undefined;
let isBlockHeight = false;
if (/^[0-9]+$/.test(blockHash)) {
isBlockHeight = true;
} else {
this.blockHash = blockHash;
}
this.isLoadingBlock = true;
this.isLoadingOverview = true;
if (isBlockHeight) {
return this.electrsApiService.getBlockHashFromHeight$(parseInt(blockHash, 10))
.pipe(
switchMap((hash) => {
if (hash) {
this.blockHash = hash;
return this.apiService.getBlock$(hash);
} else {
return null;
}
}),
catchError(() => {
return of(null);
}),
);
}
return this.apiService.getBlock$(blockHash);
}),
filter((block: BlockExtended | void) => block != null),
tap((block: BlockExtended) => {
this.block = block;
this.blockHeight = block.height;
this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`);
if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) {
this.seoService.setDescription($localize`:@@meta.description.liquid.block:See size, weight, fee range, included transactions, and more for Liquid${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`);
} else {
this.seoService.setDescription($localize`:@@meta.description.bitcoin.block:See size, weight, fee range, included transactions, audit (expected v actual), and more for Bitcoin${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`);
}
this.isLoadingBlock = false;
this.isLoadingOverview = true;
}),
shareReplay(1)
);
this.overviewSubscription = block$.pipe(
switchMap((block) => this.apiService.getStrippedBlockTransactions$(block.id)
.pipe(
catchError(() => {
return of([]);
}),
switchMap((transactions) => {
return of(transactions);
})
)
),
)
.subscribe((transactions: TransactionStripped[]) => {
this.strippedTransactions = transactions;
this.isLoadingOverview = false;
if (this.blockGraph) {
this.blockGraph.destroy();
this.blockGraph.setup(this.strippedTransactions);
}
},
() => {
this.isLoadingOverview = false;
if (this.blockGraph) {
this.blockGraph.destroy();
}
});
this.networkChangedSubscription = this.stateService.networkChanged$
.subscribe((network) => this.network = network);
}
onTxClick(event: { tx: TransactionStripped, keyModifier: boolean }): void {
const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.tx.txid}`);
if (!event.keyModifier) {
this.router.navigate([url]);
} else {
window.open(url, '_blank');
}
}
@HostListener('window:resize', ['$event'])
onResize(): void {
if (this.autofit) {
this.resolution = bestFitResolution(64, 96, Math.min(window.innerWidth, window.innerHeight));
}
}
ngOnDestroy(): void {
if (this.overviewSubscription) {
this.overviewSubscription.unsubscribe();
}
if (this.networkChangedSubscription) {
this.networkChangedSubscription.unsubscribe();
}
if (this.queryParamsSubscription) {
this.queryParamsSubscription.unsubscribe();
}
}
}

View File

@ -0,0 +1,43 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { BlockComponent } from './block.component';
import { SharedModule } from '../../shared/shared.module';
const routes: Routes = [
{
path: ':id',
component: BlockComponent,
data: {
ogImage: true
}
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class BlockRoutingModule { }
@NgModule({
imports: [
CommonModule,
BlockRoutingModule,
SharedModule,
],
declarations: [
BlockComponent,
]
})
export class BlockModule { }

View File

@ -1,6 +1,6 @@
<app-indexing-progress *ngIf="!widget"></app-indexing-progress> <app-indexing-progress *ngIf="!widget"></app-indexing-progress>
<div class="container-xl" style="min-height: 335px" [ngClass]="{'widget': widget, 'full-height': !widget, 'legacy': !indexingAvailable}"> <div class="container-xl" style="min-height: 335px" [ngClass]="{'widget': widget, 'full-height': !widget, 'legacy': !isMempoolModule}">
<h1 *ngIf="!widget" class="float-left" i18n="master-page.blocks">Blocks</h1> <h1 *ngIf="!widget" class="float-left" i18n="master-page.blocks">Blocks</h1>
<div *ngIf="!widget && isLoading" class="spinner-border ml-3" role="status"></div> <div *ngIf="!widget && isLoading" class="spinner-border ml-3" role="status"></div>
@ -9,28 +9,28 @@
<div style="min-height: 295px"> <div style="min-height: 295px">
<table class="table table-borderless"> <table class="table table-borderless">
<thead> <thead>
<th class="height text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" i18n="latest-blocks.height">Height</th> <th class="height text-left" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}" i18n="latest-blocks.height">Height</th>
<th *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" i18n="mining.pool-name" <th *ngIf="isMempoolModule" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}" i18n="mining.pool-name"
i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool</th> i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool</th>
<th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Timestamp</th> <th class="timestamp" i18n="latest-blocks.timestamp" *ngIf="!widget" [class]="isMempoolModule ? '' : 'legacy'">Timestamp</th>
<th *ngIf="auditAvailable" class="health text-right" i18n="latest-blocks.health" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" <th *ngIf="auditAvailable" class="health text-right" i18n="latest-blocks.health" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}"
i18n-ngbTooltip="latest-blocks.health" ngbTooltip="Health" placement="bottom" #health [disableTooltip]="!isEllipsisActive(health)">Health</th> i18n-ngbTooltip="latest-blocks.health" ngbTooltip="Health" placement="bottom" #health [disableTooltip]="!isEllipsisActive(health)">Health</th>
<th *ngIf="indexingAvailable" class="reward text-right" i18n="latest-blocks.reward" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" <th *ngIf="isMempoolModule" class="reward text-right" i18n="latest-blocks.reward" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}"
i18n-ngbTooltip="latest-blocks.reward" ngbTooltip="Reward" placement="bottom" #reward [disableTooltip]="!isEllipsisActive(reward)">Reward</th> i18n-ngbTooltip="latest-blocks.reward" ngbTooltip="Reward" placement="bottom" #reward [disableTooltip]="!isEllipsisActive(reward)">Reward</th>
<th *ngIf="indexingAvailable && !widget" class="fees text-right" i18n="latest-blocks.fees" [class]="indexingAvailable ? '' : 'legacy'">Fees</th> <th *ngIf="isMempoolModule && !auditAvailable || isMempoolModule && !widget" class="fees text-right" i18n="latest-blocks.fees" [class]="isMempoolModule ? '' : 'legacy'">Fees</th>
<th *ngIf="auditAvailable && !widget" class="fee-delta" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"></th> <th *ngIf="auditAvailable && !widget" class="fee-delta" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}"></th>
<th *ngIf="indexingAvailable" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}" <th *ngIf="isMempoolModule" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}"
i18n-ngbTooltip="dashboard.txs" ngbTooltip="TXs" placement="bottom" #txs [disableTooltip]="!isEllipsisActive(txs)">TXs</th> i18n-ngbTooltip="dashboard.txs" ngbTooltip="TXs" placement="bottom" #txs [disableTooltip]="!isEllipsisActive(txs)">TXs</th>
<th *ngIf="!indexingAvailable" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}">Transactions</th> <th *ngIf="!isMempoolModule" class="txs text-right" i18n="dashboard.txs" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">Transactions</th>
<th class="size" i18n="latest-blocks.size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'">Size</th> <th class="size" i18n="latest-blocks.size" *ngIf="!widget" [class]="isMempoolModule ? '' : 'legacy'">Size</th>
</thead> </thead>
<tbody *ngIf="blocks$ | async as blocks; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''"> <tbody *ngIf="blocks$ | async as blocks; else skeleton" [style]="isLoading ? 'opacity: 0.75' : ''">
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock"> <tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
<td class="height text-left" [class]="widget ? 'widget' : ''"> <td class="height text-left" [class]="widget ? 'widget' : ''">
<a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a> <a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a>
</td> </td>
<td *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td *ngIf="isMempoolModule" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<div class="tooltip-custom"> <div *ngIf="indexingAvailable" class="tooltip-custom">
<a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]"> <a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]">
<img width="22" height="22" src="{{ block.extras.pool['logo'] }}" <img width="22" height="22" src="{{ block.extras.pool['logo'] }}"
onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + block.extras.pool.name + ' mining pool'"> onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + block.extras.pool.name + ' mining pool'">
@ -38,11 +38,17 @@
</a> </a>
<span *ngIf="!widget" class="tooltiptext badge badge-secondary scriptmessage">{{ block.extras.coinbaseRaw | hex2ascii }}</span> <span *ngIf="!widget" class="tooltiptext badge badge-secondary scriptmessage">{{ block.extras.coinbaseRaw | hex2ascii }}</span>
</div> </div>
<div *ngIf="!indexingAvailable" class="tooltip-custom">
<img width="22" height="22" src="{{ block.extras.pool['logo'] }}"
onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + block.extras.pool.name + ' mining pool'">
<span class="pool-name">{{ block.extras.pool.name }}</span>
<span *ngIf="!widget" class="tooltiptext badge badge-secondary scriptmessage">{{ block.extras.coinbaseRaw | hex2ascii }}</span>
</div>
</td> </td>
<td class="timestamp" *ngIf="!widget" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td class="timestamp" *ngIf="!widget" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
&lrm;{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} &lrm;{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
</td> </td>
<td *ngIf="auditAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td *ngIf="auditAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<a <a
*ngIf="block?.extras?.matchRate != null; else nullHealth" *ngIf="block?.extras?.matchRate != null; else nullHealth"
class="health-badge badge" class="health-badge badge"
@ -56,21 +62,21 @@
<span class="health-badge badge badge-secondary" i18n="unknown">Unknown</span> <span class="health-badge badge badge-secondary" i18n="unknown">Unknown</span>
</ng-template> </ng-template>
</td> </td>
<td *ngIf="indexingAvailable" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td *ngIf="isMempoolModule" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<app-amount [satoshis]="block.extras.reward" [noFiat]="true" digitsInfo="1.2-2"></app-amount> <app-amount [satoshis]="block.extras.reward" [noFiat]="true" digitsInfo="1.2-2"></app-amount>
</td> </td>
<td *ngIf="indexingAvailable && !widget" class="fees text-right" [class]="indexingAvailable ? '' : 'legacy'"> <td *ngIf="isMempoolModule && !auditAvailable || isMempoolModule && !widget" class="fees text-right" [class]="isMempoolModule ? '' : 'legacy'">
<app-amount [satoshis]="block.extras.totalFees" [noFiat]="true" digitsInfo="1.2-2"></app-amount> <app-amount [satoshis]="block.extras.totalFees" [noFiat]="true" digitsInfo="1.2-2"></app-amount>
</td> </td>
<td *ngIf="auditAvailable" class="fee-delta" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td *ngIf="auditAvailable" class="fee-delta" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<span *ngIf="block.extras.feeDelta" class="difference" [class.positive]="block.extras.feeDelta >= 0" [class.negative]="block.extras.feeDelta < 0"> <span *ngIf="block.extras.feeDelta" class="difference" [class.positive]="block.extras.feeDelta >= 0" [class.negative]="block.extras.feeDelta < 0">
{{ block.extras.feeDelta > 0 ? '+' : '' }}{{ (block.extras.feeDelta * 100) | amountShortener: 2 }}% {{ block.extras.feeDelta > 0 ? '+' : '' }}{{ (block.extras.feeDelta * 100) | amountShortener: 2 }}%
</span> </span>
</td> </td>
<td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
{{ block.tx_count | number }} {{ block.tx_count | number }}
</td> </td>
<td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'"> <td class="size" *ngIf="!widget" [class]="isMempoolModule ? '' : 'legacy'">
<div class="progress"> <div class="progress">
<div class="progress-bar progress-mempool" role="progressbar" <div class="progress-bar progress-mempool" role="progressbar"
[ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div> [ngStyle]="{'width': (block.weight / stateService.env.BLOCK_WEIGHT_UNITS)*100 + '%' }"></div>
@ -82,34 +88,34 @@
<ng-template #skeleton> <ng-template #skeleton>
<tbody> <tbody>
<tr *ngFor="let item of skeletonLines"> <tr *ngFor="let item of skeletonLines">
<td class="height text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td class="height text-left" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td *ngIf="indexingAvailable" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td *ngIf="isMempoolModule" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<span class="skeleton-loader" style="max-width: 125px"></span> <span class="skeleton-loader" style="max-width: 125px"></span>
</td> </td>
<td class="timestamp" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'"> <td class="timestamp" *ngIf="!widget" [class]="isMempoolModule ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 150px"></span> <span class="skeleton-loader" style="max-width: 150px"></span>
</td> </td>
<td class="mined" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'"> <td class="mined" *ngIf="!widget" [class]="isMempoolModule ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 125px"></span> <span class="skeleton-loader" style="max-width: 125px"></span>
</td> </td>
<td *ngIf="auditAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td *ngIf="auditAvailable" class="health text-right" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td *ngIf="indexingAvailable" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td *ngIf="isMempoolModule" class="reward text-right" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td *ngIf="indexingAvailable && !widget" class="fees text-right" [class]="indexingAvailable ? '' : 'legacy'"> <td *ngIf="isMempoolModule && !widget" class="fees text-right" [class]="isMempoolModule ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td *ngIf="auditAvailable && !widget" class="fee-delta" [class]="indexingAvailable ? '' : 'legacy'"> <td *ngIf="auditAvailable && !widget" class="fee-delta" [class]="isMempoolModule ? '' : 'legacy'">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !indexingAvailable}"> <td class="txs text-right" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<span class="skeleton-loader" style="max-width: 75px"></span> <span class="skeleton-loader" style="max-width: 75px"></span>
</td> </td>
<td class="size" *ngIf="!widget" [class]="indexingAvailable ? '' : 'legacy'"> <td class="size" *ngIf="!widget" [class]="isMempoolModule ? '' : 'legacy'">
<span class="skeleton-loader"></span> <span class="skeleton-loader"></span>
</td> </td>
</tr> </tr>

View File

@ -19,6 +19,7 @@ export class BlocksList implements OnInit {
blocks$: Observable<BlockExtended[]> = undefined; blocks$: Observable<BlockExtended[]> = undefined;
isMempoolModule = false;
indexingAvailable = false; indexingAvailable = false;
auditAvailable = false; auditAvailable = false;
isLoading = true; isLoading = true;
@ -39,6 +40,7 @@ export class BlocksList implements OnInit {
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private seoService: SeoService, private seoService: SeoService,
) { ) {
this.isMempoolModule = this.stateService.env.BASE_MODULE === 'mempool';
} }
ngOnInit(): void { ngOnInit(): void {
@ -75,11 +77,10 @@ export class BlocksList implements OnInit {
this.lastBlockHeight = Math.max(...blocks.map(o => o.height)); this.lastBlockHeight = Math.max(...blocks.map(o => o.height));
}), }),
map(blocks => { map(blocks => {
if (this.indexingAvailable) { if (this.stateService.env.BASE_MODULE === 'mempool') {
for (const block of blocks) { for (const block of blocks) {
// @ts-ignore: Need to add an extra field for the template // @ts-ignore: Need to add an extra field for the template
block.extras.pool.logo = `/resources/mining-pools/` + block.extras.pool.logo = `/resources/mining-pools/` + block.extras.pool.slug + '.svg';
block.extras.pool.slug + '.svg';
} }
} }
if (this.widget) { if (this.widget) {
@ -110,7 +111,7 @@ export class BlocksList implements OnInit {
} }
if (blocks[1]) { if (blocks[1]) {
this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1; this.blocksCount = Math.max(this.blocksCount, blocks[1][0].height) + 1;
if (this.stateService.env.MINING_DASHBOARD) { if (this.isMempoolModule) {
// @ts-ignore: Need to add an extra field for the template // @ts-ignore: Need to add an extra field for the template
blocks[1][0].extras.pool.logo = `/resources/mining-pools/` + blocks[1][0].extras.pool.logo = `/resources/mining-pools/` +
blocks[1][0].extras.pool.slug + '.svg'; blocks[1][0].extras.pool.slug + '.svg';
@ -121,9 +122,11 @@ export class BlocksList implements OnInit {
return acc; return acc;
}, []), }, []),
switchMap((blocks) => { switchMap((blocks) => {
blocks.forEach(block => { if (this.isMempoolModule && this.auditAvailable) {
block.extras.feeDelta = block.extras.expectedFees ? (block.extras.totalFees - block.extras.expectedFees) / block.extras.expectedFees : 0; blocks.forEach(block => {
}); block.extras.feeDelta = block.extras.expectedFees ? (block.extras.totalFees - block.extras.expectedFees) / block.extras.expectedFees : 0;
});
}
return of(blocks); return of(blocks);
}) })
); );

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
import { EChartsOption, graphic } from 'echarts'; import { echarts, EChartsOption } from '../../graphs/echarts';
import { merge, Observable, of } from 'rxjs'; import { merge, Observable, of } from 'rxjs';
import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';
@ -204,7 +204,7 @@ export class HashrateChartComponent implements OnInit {
title: title, title: title,
animation: false, animation: false,
color: [ color: [
new graphic.LinearGradient(0, 0, 0, 0.65, [ new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [
{ offset: 0, color: '#F4511E99' }, { offset: 0, color: '#F4511E99' },
{ offset: 0.25, color: '#FB8C0099' }, { offset: 0.25, color: '#FB8C0099' },
{ offset: 0.5, color: '#FFB30099' }, { offset: 0.5, color: '#FFB30099' },
@ -212,7 +212,7 @@ export class HashrateChartComponent implements OnInit {
{ offset: 1, color: '#7CB34299' } { offset: 1, color: '#7CB34299' }
]), ]),
'#D81B60', '#D81B60',
new graphic.LinearGradient(0, 0, 0, 0.65, [ new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [
{ offset: 0, color: '#F4511E' }, { offset: 0, color: '#F4511E' },
{ offset: 0.25, color: '#FB8C00' }, { offset: 0.25, color: '#FB8C00' },
{ offset: 0.5, color: '#FFB300' }, { offset: 0.5, color: '#FFB300' },
@ -342,7 +342,7 @@ export class HashrateChartComponent implements OnInit {
type: 'value', type: 'value',
axisLabel: { axisLabel: {
color: 'rgb(110, 112, 121)', color: 'rgb(110, 112, 121)',
formatter: (val) => { formatter: (val): string => {
const selectedPowerOfTen: any = selectPowerOfTen(val); const selectedPowerOfTen: any = selectPowerOfTen(val);
const newVal = Math.round(val / selectedPowerOfTen.divider); const newVal = Math.round(val / selectedPowerOfTen.divider);
return `${newVal} ${selectedPowerOfTen.unit}H/s`; return `${newVal} ${selectedPowerOfTen.unit}H/s`;
@ -364,9 +364,9 @@ export class HashrateChartComponent implements OnInit {
position: 'right', position: 'right',
axisLabel: { axisLabel: {
color: 'rgb(110, 112, 121)', color: 'rgb(110, 112, 121)',
formatter: (val) => { formatter: (val): string => {
if (this.stateService.network === 'signet') { if (this.stateService.network === 'signet') {
return val; return `${val}`;
} }
const selectedPowerOfTen: any = selectPowerOfTen(val); const selectedPowerOfTen: any = selectPowerOfTen(val);
const newVal = Math.round(val / selectedPowerOfTen.divider); const newVal = Math.round(val / selectedPowerOfTen.divider);

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { delay, map, retryWhen, share, startWith, switchMap, tap } from 'rxjs/operators'; import { delay, map, retryWhen, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';

View File

@ -1,5 +1,5 @@
import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
import { OnChanges } from '@angular/core'; import { OnChanges } from '@angular/core';
import { StorageService } from '../../services/storage.service'; import { StorageService } from '../../services/storage.service';
import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils'; import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils';
@ -37,6 +37,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
}; };
windowPreference: string; windowPreference: string;
chartInstance: any = undefined; chartInstance: any = undefined;
MA: number[][] = [];
weightMode: boolean = false; weightMode: boolean = false;
rateUnitSub: Subscription; rateUnitSub: Subscription;
@ -62,6 +63,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
return; return;
} }
this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference');
this.MA = this.calculateMA(this.data.series[0]);
this.mountChart(); this.mountChart();
} }
@ -72,7 +74,101 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
this.isLoading = false; this.isLoading = false;
} }
/// calculate the moving average of maData
calculateMA(maData): number[][] {
//update const variables that are not changed
const ma: number[][] = [];
let sum = 0;
let i = 0;
const len = maData.length;
//Adjust window length based on the length of the data
//5% appeared as a good amount from tests
//TODO: make this a text box in the UI
const maWindowLen = Math.ceil(len * 0.05);
//calculate the center of the moving average window
const center = Math.floor(maWindowLen / 2);
//calculate the centered moving average
for (i = center; i < len - center; i++) {
sum = 0;
//build out ma as we loop through the data
ma[i] = [];
ma[i].push(maData[i][0]);
for (let j = i - center; j <= i + center; j++) {
sum += maData[j][1];
}
ma[i].push(sum / maWindowLen);
}
//return the moving average array
return ma;
}
mountChart(): void { mountChart(): void {
//create an array for the echart series
//similar to how it is done in mempool-graph.component.ts
const seriesGraph = [];
seriesGraph.push({
zlevel: 0,
name: 'data',
data: this.data.series[0],
type: 'line',
smooth: false,
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 3,
},
markLine: {
silent: true,
symbol: 'none',
lineStyle: {
color: '#fff',
opacity: 1,
width: 2,
},
data: [{
yAxis: 1667,
label: {
show: false,
color: '#ffffff',
}
}],
}
},
{
zlevel: 0,
name: 'MA',
data: this.MA,
type: 'line',
smooth: false,
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 1,
color: "white",
},
markLine: {
silent: true,
symbol: 'none',
lineStyle: {
color: '#fff',
opacity: 1,
width: 2,
},
data: [{
yAxis: 1667,
label: {
show: false,
color: '#ffffff',
}
}],
}
});
this.mempoolStatsChartOption = { this.mempoolStatsChartOption = {
grid: { grid: {
height: this.height, height: this.height,
@ -122,16 +218,20 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
type: 'line', type: 'line',
}, },
formatter: (params: any) => { formatter: (params: any) => {
const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue); const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue);
const colorSpan = (color: string) => `<span class="indicator" style="background-color: ` + color + `"></span>`; const colorSpan = (color: string) => `<span class="indicator" style="background-color: ` + color + `"></span>`;
let itemFormatted = '<div class="title">' + axisValueLabel + '</div>'; let itemFormatted = '<div class="title">' + axisValueLabel + '</div>';
params.map((item: any, index: number) => { params.map((item: any, index: number) => {
if (index < 26) {
itemFormatted += `<div class="item"> //Do no include MA in tooltip legend!
<div class="indicator-container">${colorSpan(item.color)}</div> if (item.seriesName !== 'MA') {
<div class="grow"></div> if (index < 26) {
<div class="value">${formatNumber(this.weightMode ? item.value[1] * 4 : item.value[1], this.locale, '1.0-0')} <span class="symbol">${this.weightMode ? 'WU' : 'vB'}/s</span></div> itemFormatted += `<div class="item">
</div>`; <div class="indicator-container">${colorSpan(item.color)}</div>
<div class="grow"></div>
<div class="value">${formatNumber(item.value[1], this.locale, '1.0-0')}<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>`; return `<div class="tx-wrapper-tooltip-chart ${(this.template === 'advanced') ? 'tx-wrapper-tooltip-chart-advanced' : ''}">${itemFormatted}</div>`;
@ -171,35 +271,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On
} }
} }
}, },
series: [ series: seriesGraph,
{
zlevel: 0,
data: this.data.series[0],
type: 'line',
smooth: false,
showSymbol: false,
symbol: 'none',
lineStyle: {
width: 3,
},
markLine: {
silent: true,
symbol: 'none',
lineStyle: {
color: '#fff',
opacity: 1,
width: 2,
},
data: [{
yAxis: 1667,
label: {
show: false,
color: '#ffffff',
}
}],
}
},
],
visualMap: { visualMap: {
show: false, show: false,
top: 50, top: 50,

View File

@ -1,6 +1,6 @@
import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges, OnInit } from '@angular/core'; import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges, OnInit } from '@angular/core';
import { formatDate, formatNumber } from '@angular/common'; import { formatDate, formatNumber } from '@angular/common';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
@Component({ @Component({
selector: 'app-lbtc-pegs-graph', selector: 'app-lbtc-pegs-graph',

View File

@ -0,0 +1,5 @@
<div class="block-wrapper">
<div class="block-container">
<app-mempool-block-overview [index]="index"></app-mempool-block-overview>
</div>
</div>

View File

@ -0,0 +1,22 @@
.block-wrapper {
width: 100vw;
height: 100vh;
background: #181b2d;
}
.block-container {
flex-grow: 0;
flex-shrink: 0;
width: 100vw;
max-width: 100vh;
height: 100vh;
padding: 0;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
* {
flex-grow: 1;
}
}

View File

@ -0,0 +1,85 @@
import { Component, OnInit, OnDestroy, HostListener } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Subscription, filter, map, switchMap, tap } from 'rxjs';
import { StateService } from '../../services/state.service';
import { WebsocketService } from '../../services/websocket.service';
function bestFitResolution(min, max, n): number {
const target = (min + max) / 2;
let bestScore = Infinity;
let best = null;
for (let i = min; i <= max; i++) {
const remainder = (n % i);
if (remainder < bestScore || (remainder === bestScore && (Math.abs(i - target) < Math.abs(best - target)))) {
bestScore = remainder;
best = i;
}
}
return best;
}
@Component({
selector: 'app-mempool-block-view',
templateUrl: './mempool-block-view.component.html',
styleUrls: ['./mempool-block-view.component.scss']
})
export class MempoolBlockViewComponent implements OnInit, OnDestroy {
autofit: boolean = false;
resolution: number = 80;
index: number = 0;
routeParamsSubscription: Subscription;
queryParamsSubscription: Subscription;
constructor(
private route: ActivatedRoute,
private websocketService: WebsocketService,
public stateService: StateService,
) { }
ngOnInit(): void {
this.websocketService.want(['blocks', 'mempool-blocks']);
this.routeParamsSubscription = this.route.paramMap
.pipe(
switchMap((params: ParamMap) => {
this.index = parseInt(params.get('index'), 10) || 0;
return this.stateService.mempoolBlocks$
.pipe(
map((blocks) => {
if (!blocks.length) {
return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }];
}
return blocks;
}),
filter((mempoolBlocks) => mempoolBlocks.length > 0),
tap((mempoolBlocks) => {
while (!mempoolBlocks[this.index]) {
this.index--;
}
})
);
})
).subscribe();
this.queryParamsSubscription = this.route.queryParams.subscribe((params) => {
this.autofit = params.autofit === 'true';
if (this.autofit) {
this.onResize();
}
});
}
@HostListener('window:resize', ['$event'])
onResize(): void {
if (this.autofit) {
this.resolution = bestFitResolution(64, 96, Math.min(window.innerWidth, window.innerHeight));
}
}
ngOnDestroy(): void {
this.routeParamsSubscription.unsubscribe();
this.queryParamsSubscription.unsubscribe();
}
}

View File

@ -6,7 +6,7 @@ import { formatNumber } from '@angular/common';
import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; import { OptimizedMempoolStats } from '../../interfaces/node-api.interface';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
import { StorageService } from '../../services/storage.service'; import { StorageService } from '../../services/storage.service';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
import { feeLevels, chartColors } from '../../app.constants'; import { feeLevels, chartColors } from '../../app.constants';
import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils'; import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils';
@ -27,7 +27,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
@Input() data: any[]; @Input() data: any[];
@Input() filterSize = 100000; @Input() filterSize = 100000;
@Input() limitFilterFee = 1; @Input() limitFilterFee = 1;
@Input() hideCount: boolean = false; @Input() hideCount: boolean = true;
@Input() height: number | string = 200; @Input() height: number | string = 200;
@Input() top: number | string = 20; @Input() top: number | string = 20;
@Input() right: number | string = 10; @Input() right: number | string = 10;
@ -53,7 +53,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges {
chartInstance: any = undefined; chartInstance: any = undefined;
weightMode: boolean = false; weightMode: boolean = false;
isWidget: boolean = false; isWidget: boolean = false;
showCount: boolean = true; showCount: boolean = false;
constructor( constructor(
private vbytesPipe: VbytesPipe, private vbytesPipe: VbytesPipe,

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, Input, NgZone, OnInit, HostBinding } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, NgZone, OnInit, HostBinding } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { EChartsOption, PieSeriesOption } from 'echarts'; import { EChartsOption, PieSeriesOption } from '../../graphs/echarts';
import { merge, Observable } from 'rxjs'; import { merge, Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { SeoService } from '../../services/seo.service'; import { SeoService } from '../../services/seo.service';

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { EChartsOption, graphic } from 'echarts'; import { echarts, EChartsOption } from '../../graphs/echarts';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map, switchMap, catchError } from 'rxjs/operators'; import { map, switchMap, catchError } from 'rxjs/operators';
import { PoolStat } from '../../interfaces/node-api.interface'; import { PoolStat } from '../../interfaces/node-api.interface';
@ -127,7 +127,7 @@ export class PoolPreviewComponent implements OnInit {
title: title, title: title,
animation: false, animation: false,
color: [ color: [
new graphic.LinearGradient(0, 0, 0, 0.65, [ new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [
{ offset: 0, color: '#F4511E' }, { offset: 0, color: '#F4511E' },
{ offset: 0.25, color: '#FB8C00' }, { offset: 0.25, color: '#FB8C00' },
{ offset: 0.5, color: '#FFB300' }, { offset: 0.5, color: '#FFB300' },

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { EChartsOption, graphic } from 'echarts'; import { echarts, EChartsOption } from '../../graphs/echarts';
import { BehaviorSubject, Observable, of, timer } from 'rxjs'; import { BehaviorSubject, Observable, of, timer } from 'rxjs';
import { catchError, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { catchError, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
import { BlockExtended, PoolStat } from '../../interfaces/node-api.interface'; import { BlockExtended, PoolStat } from '../../interfaces/node-api.interface';
@ -131,7 +131,7 @@ export class PoolComponent implements OnInit {
title: title, title: title,
animation: false, animation: false,
color: [ color: [
new graphic.LinearGradient(0, 0, 0, 0.65, [ new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [
{ offset: 0, color: '#F4511E' }, { offset: 0, color: '#F4511E' },
{ offset: 0.25, color: '#FB8C00' }, { offset: 0.25, color: '#FB8C00' },
{ offset: 0.5, color: '#FFB300' }, { offset: 0.5, color: '#FFB300' },

View File

@ -0,0 +1,40 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { PrivacyPolicyComponent } from './privacy-policy.component';
import { SharedModule } from '../../shared/shared.module';
const routes: Routes = [
{
path: '',
component: PrivacyPolicyComponent,
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class PrivacyPolicyRoutingModule { }
@NgModule({
imports: [
CommonModule,
PrivacyPolicyRoutingModule,
SharedModule,
],
declarations: [
PrivacyPolicyComponent,
]
})
export class PrivacyPolicyModule { }

View File

@ -32,7 +32,7 @@ export class StatisticsComponent implements OnInit {
chartColors = chartColors; chartColors = chartColors;
filterSize = 100000; filterSize = 100000;
filterFeeIndex = 1; filterFeeIndex = 1;
showCount = true; showCount = false;
maxFeeIndex: number; maxFeeIndex: number;
dropDownOpen = false; dropDownOpen = false;

View File

@ -0,0 +1,40 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { TermsOfServiceComponent } from './terms-of-service.component';
import { SharedModule } from '../../shared/shared.module';
const routes: Routes = [
{
path: '',
component: TermsOfServiceComponent,
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class TermsModule { }
@NgModule({
imports: [
CommonModule,
TermsModule,
SharedModule,
],
declarations: [
TermsOfServiceComponent,
]
})
export class TermsOfServiceModule { }

View File

@ -0,0 +1,40 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { TrademarkPolicyComponent } from './trademark-policy.component';
import { SharedModule } from '../../shared/shared.module';
const routes: Routes = [
{
path: '',
component: TrademarkPolicyComponent,
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class TrademarkRoutingModule { }
@NgModule({
imports: [
CommonModule,
TrademarkRoutingModule,
SharedModule,
],
declarations: [
TrademarkPolicyComponent,
]
})
export class TrademarkModule { }

View File

@ -0,0 +1,45 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { TransactionComponent } from './transaction.component';
import { SharedModule } from '../../shared/shared.module';
import { TxBowtieModule } from '../tx-bowtie-graph/tx-bowtie.module';
const routes: Routes = [
{
path: ':id',
component: TransactionComponent,
data: {
ogImage: true
}
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class TransactionRoutingModule { }
@NgModule({
imports: [
CommonModule,
TransactionRoutingModule,
SharedModule,
TxBowtieModule,
],
declarations: [
TransactionComponent,
]
})
export class TransactionModule { }

View File

@ -81,7 +81,7 @@
</ng-container> </ng-container>
</div> </div>
</td> </td>
<td class="text-right nowrap amount"> <td class="text-right nowrap amount" [class]="{large: vin?.prevout?.value > 1000000000}">
<ng-template [ngIf]="vin.prevout && vin.prevout.asset && vin.prevout.asset !== nativeAssetId" [ngIfElse]="defaultOutput"> <ng-template [ngIf]="vin.prevout && vin.prevout.asset && vin.prevout.asset !== nativeAssetId" [ngIfElse]="defaultOutput">
<div *ngIf="assetsMinimal && assetsMinimal[vin.prevout.asset] else assetVinNotFound"> <div *ngIf="assetsMinimal && assetsMinimal[vin.prevout.asset] else assetVinNotFound">
<ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: vin.prevout }"></ng-container> <ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: vin.prevout }"></ng-container>
@ -222,7 +222,7 @@
</ng-template> </ng-template>
</ng-template> </ng-template>
</td> </td>
<td class="text-right nowrap amount"> <td class="text-right nowrap amount" [class]="{large: vout?.value > 1000000000}">
<ng-template [ngIf]="vout.asset && vout.asset !== nativeAssetId" [ngIfElse]="defaultOutput"> <ng-template [ngIf]="vout.asset && vout.asset !== nativeAssetId" [ngIfElse]="defaultOutput">
<div *ngIf="assetsMinimal && assetsMinimal[vout.asset] else assetNotFound"> <div *ngIf="assetsMinimal && assetsMinimal[vout.asset] else assetNotFound">
<ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: vout }"></ng-container> <ng-container *ngTemplateOutlet="assetBox; context:{ $implicit: vout }"></ng-container>

View File

@ -46,7 +46,16 @@
} }
td.amount { td.amount {
width: 32.5%; width: 36%;
@media (max-width: 576px) {
width: 50%;
}
}
td.amount.large {
width: 45%;
@media (max-width: 576px) {
width: 60%;
}
} }
.extra-info { .extra-info {

View File

@ -0,0 +1,28 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../../shared/shared.module';
import { TxBowtieGraphComponent } from '../tx-bowtie-graph/tx-bowtie-graph.component';
import { TxBowtieGraphTooltipComponent } from '../tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component';
@NgModule({
imports: [
CommonModule,
SharedModule,
],
declarations: [
TxBowtieGraphComponent,
TxBowtieGraphTooltipComponent,
],
exports: [
TxBowtieGraphComponent,
TxBowtieGraphTooltipComponent,
]
})
export class TxBowtieModule { }

View File

@ -137,8 +137,16 @@
<ng-template type="what-is-a-mempool-explorer"> <ng-template type="what-is-a-mempool-explorer">
<p>A mempool explorer is a tool that enables you to view real-time and historical information about a node's mempool, visualize its transactions, and search and view those transactions.</p><p>The mempool.space website invented the concept of visualizing a Bitcoin node's mempool as <b>projected blocks</b>. These blocks are the inspiration for our half-filled block logo.</p><p>Projected blocks are on the left of the dotted white line, and confirmed blocks are on the right.</p> <p>A mempool explorer is a tool that enables you to view real-time and historical information about a node's mempool, visualize its transactions, and search and view those transactions.</p><p>The mempool.space website invented the concept of visualizing a Bitcoin node's mempool as <b>projected blocks</b>. These blocks are the inspiration for our half-filled block logo.</p><p>Projected blocks are on the left of the dotted white line, and confirmed blocks are on the right.</p>
<div class="blockchain-wrapper"> <div class="blockchain-wrapper" [dir]="timeLtr ? 'rtl' : 'ltr'" [class.time-ltr]="timeLtr">
<app-blockchain></app-blockchain> <div class="position-container">
<span>
<div class="blocks-wrapper">
<app-mempool-blocks></app-mempool-blocks>
<app-blockchain-blocks></app-blockchain-blocks>
</div>
<div id="divider"></div>
</span>
</div>
</div> </div>
</ng-template> </ng-template>

View File

@ -259,13 +259,46 @@ h3 {
} }
.blockchain-wrapper { .blockchain-wrapper {
position: relative; display: block;
height: 100%;
width: 100%; width: 100%;
overflow: auto; min-height: 220px;
scrollbar-width: none; position: relative;
overflow: hidden;
.position-container {
position: absolute;
left: 50%;
bottom: 150px;
}
#divider {
width: 2px;
height: 175px;
left: 0;
top: -40px;
position: absolute;
}
&.time-ltr {
.blocks-wrapper {
transform: scaleX(-1);
}
}
} }
.blockchain-wrapper::-webkit-scrollbar {
display: none; :host-context(.ltr-layout) {
.blockchain-wrapper.time-ltr .blocks-wrapper,
.blockchain-wrapper .blocks-wrapper {
direction: ltr;
}
}
:host-context(.rtl-layout) {
.blockchain-wrapper.time-ltr .blocks-wrapper,
.blockchain-wrapper .blocks-wrapper {
direction: rtl;
}
} }
#disclaimer { #disclaimer {

View File

@ -1,6 +1,6 @@
import { Component, OnInit, Input, QueryList, AfterViewInit, ViewChildren } from '@angular/core'; import { Component, OnInit, Input, QueryList, AfterViewInit, ViewChildren } from '@angular/core';
import { Env, StateService } from '../../services/state.service'; import { Env, StateService } from '../../services/state.service';
import { Observable, merge, of, Subject } from 'rxjs'; import { Observable, merge, of, Subject, Subscription } from 'rxjs';
import { tap, takeUntil } from 'rxjs/operators'; import { tap, takeUntil } from 'rxjs/operators';
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { faqData, restApiDocsData, wsApiDocsData } from './api-docs-data'; import { faqData, restApiDocsData, wsApiDocsData } from './api-docs-data';
@ -30,6 +30,8 @@ export class ApiDocsComponent implements OnInit, AfterViewInit {
officialMempoolInstance: boolean; officialMempoolInstance: boolean;
auditEnabled: boolean; auditEnabled: boolean;
mobileViewport: boolean = false; mobileViewport: boolean = false;
timeLtrSubscription: Subscription;
timeLtr: boolean = this.stateService.timeLtr.value;
@ViewChildren(FaqTemplateDirective) faqTemplates: QueryList<FaqTemplateDirective>; @ViewChildren(FaqTemplateDirective) faqTemplates: QueryList<FaqTemplateDirective>;
dict = {}; dict = {};
@ -104,12 +106,17 @@ export class ApiDocsComponent implements OnInit, AfterViewInit {
this.electrsPort = 51302; break; this.electrsPort = 51302; break;
} }
}); });
this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {
this.timeLtr = !!ltr;
});
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.destroy$.next(true); this.destroy$.next(true);
this.destroy$.complete(); this.destroy$.complete();
window.removeEventListener('scroll', this.onDocScroll); window.removeEventListener('scroll', this.onDocScroll);
this.timeLtrSubscription.unsubscribe();
} }
onDocScroll() { onDocScroll() {

View File

@ -0,0 +1,17 @@
// Import tree-shakeable echarts
import * as echarts from 'echarts/core';
import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart } from 'echarts/charts';
import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent } from 'echarts/components';
import { SVGRenderer, CanvasRenderer } from 'echarts/renderers';
// Typescript interfaces
import { EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption } from 'echarts';
echarts.use([
SVGRenderer, CanvasRenderer,
TitleComponent, TooltipComponent, GridComponent,
LegendComponent, GeoComponent, DataZoomComponent,
VisualMapComponent,
LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart
]);
export { echarts, EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption };

View File

@ -53,7 +53,7 @@ import { CommonModule } from '@angular/common';
SharedModule, SharedModule,
GraphsRoutingModule, GraphsRoutingModule,
NgxEchartsModule.forRoot({ NgxEchartsModule.forRoot({
echarts: () => import('echarts') echarts: () => import('./echarts').then(m => m.echarts),
}) })
], ],
exports: [ exports: [

View File

@ -8,8 +8,6 @@ import { BlockSizesWeightsGraphComponent } from '../components/block-sizes-weigh
import { GraphsComponent } from '../components/graphs/graphs.component'; import { GraphsComponent } from '../components/graphs/graphs.component';
import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component'; import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component';
import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component';
import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component';
import { MasterPageComponent } from '../components/master-page/master-page.component';
import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component'; import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component';
import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component';
import { PoolRankingComponent } from '../components/pool-ranking/pool-ranking.component'; import { PoolRankingComponent } from '../components/pool-ranking/pool-ranking.component';
@ -18,22 +16,10 @@ import { StartComponent } from '../components/start/start.component';
import { StatisticsComponent } from '../components/statistics/statistics.component'; import { StatisticsComponent } from '../components/statistics/statistics.component';
import { TelevisionComponent } from '../components/television/television.component'; import { TelevisionComponent } from '../components/television/television.component';
import { DashboardComponent } from '../dashboard/dashboard.component'; import { DashboardComponent } from '../dashboard/dashboard.component';
import { NodesNetworksChartComponent } from '../lightning/nodes-networks-chart/nodes-networks-chart.component';
import { LightningStatisticsChartComponent } from '../lightning/statistics-chart/lightning-statistics-chart.component';
import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component';
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
const browserWindow = window || {};
// @ts-ignore
const browserWindowEnv = browserWindow.__env || {};
const isLiquid = browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid';
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
component: isLiquid ? LiquidMasterPageComponent : MasterPageComponent,
children: [ children: [
{ {
path: 'mining/pool/:slug', path: 'mining/pool/:slug',
@ -108,34 +94,9 @@ const routes: Routes = [
component: BlockSizesWeightsGraphComponent, component: BlockSizesWeightsGraphComponent,
}, },
{ {
path: 'lightning/nodes-networks', path: 'lightning',
data: { networks: ['bitcoin'] }, data: { preload: true, networks: ['bitcoin'] },
component: NodesNetworksChartComponent, loadChildren: () => import ('./lightning-graphs.module').then(m => m.LightningGraphsModule),
},
{
path: 'lightning/capacity',
data: { networks: ['bitcoin'] },
component: LightningStatisticsChartComponent,
},
{
path: 'lightning/nodes-per-isp',
data: { networks: ['bitcoin'] },
component: NodesPerISPChartComponent,
},
{
path: 'lightning/nodes-per-country',
data: { networks: ['bitcoin'] },
component: NodesPerCountryChartComponent,
},
{
path: 'lightning/nodes-map',
data: { networks: ['bitcoin'] },
component: NodesMap,
},
{
path: 'lightning/nodes-channels-map',
data: { networks: ['bitcoin'] },
component: NodesChannelsMap,
}, },
{ {
path: '', path: '',

View File

@ -0,0 +1,58 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { NodesNetworksChartComponent } from '../lightning/nodes-networks-chart/nodes-networks-chart.component';
import { LightningStatisticsChartComponent } from '../lightning/statistics-chart/lightning-statistics-chart.component';
import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component';
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
const routes: Routes = [
{
path: 'nodes-networks',
data: { networks: ['bitcoin'] },
component: NodesNetworksChartComponent,
},
{
path: 'capacity',
data: { networks: ['bitcoin'] },
component: LightningStatisticsChartComponent,
},
{
path: 'nodes-per-isp',
data: { networks: ['bitcoin'] },
component: NodesPerISPChartComponent,
},
{
path: 'nodes-per-country',
data: { networks: ['bitcoin'] },
component: NodesPerCountryChartComponent,
},
{
path: 'nodes-map',
data: { networks: ['bitcoin'] },
component: NodesMap,
},
{
path: 'nodes-channels-map',
data: { networks: ['bitcoin'] },
component: NodesChannelsMap,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class LightningGraphsRoutingModule { }
@NgModule({
imports: [
CommonModule,
SharedModule,
LightningGraphsRoutingModule,
],
})
export class LightningGraphsModule { }

View File

@ -57,7 +57,7 @@ export class GroupPreviewComponent implements OnInit {
return of(null); return of(null);
} }
return this.lightningApiService.getNodGroupNodes$(this.groupId); return this.lightningApiService.getNodeGroup$(this.groupId);
}), }),
map((nodes) => { map((nodes) => {
for (const node of nodes) { for (const node of nodes) {

View File

@ -41,7 +41,7 @@ export class GroupComponent implements OnInit {
this.seoService.setTitle(`Mempool.space Lightning Nodes`); this.seoService.setTitle(`Mempool.space Lightning Nodes`);
this.seoService.setDescription(`See all Lightning nodes run by mempool.space -- these are the nodes that provide the data on the mempool.space Lightning dashboard.`); this.seoService.setDescription(`See all Lightning nodes run by mempool.space -- these are the nodes that provide the data on the mempool.space Lightning dashboard.`);
this.nodes$ = this.lightningApiService.getNodGroupNodes$('mempool.space') this.nodes$ = this.lightningApiService.getNodeGroup$('mempool.space')
.pipe( .pipe(
map((nodes) => { map((nodes) => {
for (const node of nodes) { for (const node of nodes) {

View File

@ -27,7 +27,7 @@ export class LightningApiService {
return this.httpClient.get<any>(this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey); return this.httpClient.get<any>(this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey);
} }
getNodGroupNodes$(name: string): Observable<any[]> { getNodeGroup$(name: string): Observable<any[]> {
return this.httpClient.get<any[]>(this.apiBasePath + '/api/v1/lightning/nodes/group/' + name); return this.httpClient.get<any[]>(this.apiBasePath + '/api/v1/lightning/nodes/group/' + name);
} }

View File

@ -1,5 +1,5 @@
import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { download } from '../../shared/graphs.utils'; import { download } from '../../shared/graphs.utils';
import { LightningApiService } from '../lightning-api.service'; import { LightningApiService } from '../lightning-api.service';

View File

@ -1,5 +1,5 @@
import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
import { EChartsOption } from 'echarts'; import { EChartsOption } from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators'; import { switchMap, tap } from 'rxjs/operators';
import { formatNumber } from '@angular/common'; import { formatNumber } from '@angular/common';

View File

@ -6,8 +6,7 @@ import { AssetsService } from '../../services/assets.service';
import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
import { EChartsOption, registerMap } from 'echarts'; import { EChartsOption, echarts } from '../../graphs/echarts';
import 'echarts-gl';
import { isMobile } from '../../shared/common.utils'; import { isMobile } from '../../shared/common.utils';
@Component({ @Component({
@ -88,7 +87,7 @@ export class NodesChannelsMap implements OnInit {
this.style !== 'channelpage' ? this.apiService.getChannelsGeo$(params.get('public_key') ?? undefined, this.style) : [''], this.style !== 'channelpage' ? this.apiService.getChannelsGeo$(params.get('public_key') ?? undefined, this.style) : [''],
[params.get('public_key') ?? undefined] [params.get('public_key') ?? undefined]
).pipe(tap((data) => { ).pipe(tap((data) => {
registerMap('world', data[0]); echarts.registerMap('world', data[0]);
const channelsLoc = []; const channelsLoc = [];
const nodes = []; const nodes = [];

View File

@ -1,7 +1,7 @@
import { formatNumber } from '@angular/common'; import { formatNumber } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ECharts, EChartsOption, TreemapSeriesOption } from 'echarts'; import { EChartsOption, TreemapSeriesOption } from '../../graphs/echarts';
import { Observable, share, switchMap, tap } from 'rxjs'; import { Observable, share, switchMap, tap } from 'rxjs';
import { lerpColor } from '../../shared/graphs.utils'; import { lerpColor } from '../../shared/graphs.utils';
import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe';
@ -18,7 +18,7 @@ import { StateService } from '../../services/state.service';
export class NodeChannels implements OnChanges { export class NodeChannels implements OnChanges {
@Input() publicKey: string; @Input() publicKey: string;
chartInstance: ECharts; chartInstance: any;
chartOptions: EChartsOption = {}; chartOptions: EChartsOption = {};
chartInitOptions = { chartInitOptions = {
renderer: 'svg', renderer: 'svg',
@ -129,7 +129,7 @@ export class NodeChannels implements OnChanges {
}; };
} }
onChartInit(ec: ECharts): void { onChartInit(ec: any): void {
this.chartInstance = ec; this.chartInstance = ec;
this.chartInstance.on('click', (e) => { this.chartInstance.on('click', (e) => {

View File

@ -3,7 +3,7 @@ import { SeoService } from '../../services/seo.service';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';
import { Observable, BehaviorSubject, switchMap, tap, combineLatest } from 'rxjs'; import { Observable, BehaviorSubject, switchMap, tap, combineLatest } from 'rxjs';
import { AssetsService } from '../../services/assets.service'; import { AssetsService } from '../../services/assets.service';
import { EChartsOption, registerMap } from 'echarts'; import { EChartsOption, echarts } from '../../graphs/echarts';
import { lerpColor } from '../../shared/graphs.utils'; import { lerpColor } from '../../shared/graphs.utils';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
@ -63,7 +63,7 @@ export class NodesMap implements OnInit, OnChanges {
this.assetsService.getWorldMapJson$, this.assetsService.getWorldMapJson$,
this.nodes$ this.nodes$
).pipe(tap((data) => { ).pipe(tap((data) => {
registerMap('world', data[0]); echarts.registerMap('world', data[0]);
let maxLiquidity = data[1].maxLiquidity; let maxLiquidity = data[1].maxLiquidity;
let inputNodes: any[] = data[1].nodes; let inputNodes: any[] = data[1].nodes;
@ -88,7 +88,7 @@ export class NodesMap implements OnInit, OnChanges {
node.public_key, node.public_key,
node.alias, node.alias,
node.capacity, node.capacity,
node.channels, node.active_channel_count,
node.country, node.country,
node.iso_code, node.iso_code,
]); ]);

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
import { EChartsOption, graphic, LineSeriesOption} from 'echarts'; import { echarts, EChartsOption, LineSeriesOption } from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { formatNumber } from '@angular/common'; import { formatNumber } from '@angular/common';
@ -152,7 +152,7 @@ export class NodesNetworksChartComponent implements OnInit {
opacity: 0.5, opacity: 0.5,
}, },
stack: 'Total', stack: 'Total',
color: new graphic.LinearGradient(0, 0.75, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [
{ offset: 0, color: '#D81B60' }, { offset: 0, color: '#D81B60' },
{ offset: 1, color: '#D81B60AA' }, { offset: 1, color: '#D81B60AA' },
]), ]),
@ -174,7 +174,7 @@ export class NodesNetworksChartComponent implements OnInit {
opacity: 0.5, opacity: 0.5,
}, },
stack: 'Total', stack: 'Total',
color: new graphic.LinearGradient(0, 0.75, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [
{ offset: 0, color: '#be7d4c' }, { offset: 0, color: '#be7d4c' },
{ offset: 1, color: '#be7d4cAA' }, { offset: 1, color: '#be7d4cAA' },
]), ]),
@ -195,7 +195,7 @@ export class NodesNetworksChartComponent implements OnInit {
opacity: 0.5, opacity: 0.5,
}, },
stack: 'Total', stack: 'Total',
color: new graphic.LinearGradient(0, 0.75, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [
{ offset: 0, color: '#FFB300' }, { offset: 0, color: '#FFB300' },
{ offset: 1, color: '#FFB300AA' }, { offset: 1, color: '#FFB300AA' },
]), ]),
@ -216,7 +216,7 @@ export class NodesNetworksChartComponent implements OnInit {
opacity: 0.5, opacity: 0.5,
}, },
stack: 'Total', stack: 'Total',
color: new graphic.LinearGradient(0, 0.75, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [
{ offset: 0, color: '#7D4698' }, { offset: 0, color: '#7D4698' },
{ offset: 1, color: '#7D4698AA' }, { offset: 1, color: '#7D4698AA' },
]), ]),

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { EChartsOption, PieSeriesOption } from 'echarts'; import { EChartsOption, PieSeriesOption } from '../../graphs/echarts';
import { map, Observable, share, tap } from 'rxjs'; import { map, Observable, share, tap } from 'rxjs';
import { chartColors } from '../../app.constants'; import { chartColors } from '../../app.constants';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone, Input } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { EChartsOption, PieSeriesOption } from 'echarts'; import { EChartsOption, PieSeriesOption } from '../../graphs/echarts';
import { combineLatest, map, Observable, share, startWith, Subject, switchMap, tap } from 'rxjs'; import { combineLatest, map, Observable, share, startWith, Subject, switchMap, tap } from 'rxjs';
import { chartColors } from '../../app.constants'; import { chartColors } from '../../app.constants';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';

View File

@ -1,5 +1,5 @@
import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core';
import { EChartsOption, graphic } from 'echarts'; import { echarts, EChartsOption } from '../../graphs/echarts';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { SeoService } from '../../services/seo.service'; import { SeoService } from '../../services/seo.service';
@ -132,7 +132,7 @@ export class LightningStatisticsChartComponent implements OnInit {
animation: false, animation: false,
color: [ color: [
'#FFB300', '#FFB300',
new graphic.LinearGradient(0, 0.75, 0, 1, [ new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [
{ offset: 0, color: '#D81B60' }, { offset: 0, color: '#D81B60' },
{ offset: 1, color: '#D81B60AA' }, { offset: 1, color: '#D81B60AA' },
]), ]),

View File

@ -0,0 +1,37 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component';
const routes: Routes = [
{
path: '',
component: LiquidMasterPageComponent,
loadChildren: () => import('../graphs/graphs.module').then(m => m.GraphsModule),
data: { preload: true },
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class LiquidGraphsRoutingModule { }
@NgModule({
imports: [
CommonModule,
LiquidGraphsRoutingModule,
],
})
export class LiquidGraphsModule { }

View File

@ -0,0 +1,125 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component';
import { StartComponent } from '../components/start/start.component';
import { AddressComponent } from '../components/address/address.component';
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
import { BlocksList } from '../components/blocks-list/blocks-list.component';
import { AssetGroupComponent } from '../components/assets/asset-group/asset-group.component';
import { AssetsComponent } from '../components/assets/assets.component';
import { AssetComponent } from '../components/asset/asset.component';
import { AssetsNavComponent } from '../components/assets/assets-nav/assets-nav.component';
const routes: Routes = [
{
path: '',
component: LiquidMasterPageComponent,
children: [
{
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule),
},
{
path: 'blocks',
component: BlocksList,
},
{
path: 'terms-of-service',
loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule),
},
{
path: 'privacy-policy',
loadChildren: () => import('../components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule),
},
{
path: 'trademark-policy',
loadChildren: () => import('../components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule),
},
{
path: 'address/:id',
children: [],
component: AddressComponent,
data: {
ogImage: true,
networkSpecific: true,
}
},
{
path: 'tx',
component: StartComponent,
data: { preload: true, networkSpecific: true },
loadChildren: () => import('../components/transaction/transaction.module').then(m => m.TransactionModule),
},
{
path: 'block',
component: StartComponent,
data: { preload: true, networkSpecific: true },
loadChildren: () => import('../components/block/block.module').then(m => m.BlockModule),
},
{
path: 'assets',
data: { networks: ['liquid'] },
component: AssetsNavComponent,
children: [
{
path: 'all',
data: { networks: ['liquid'] },
component: AssetsComponent,
},
{
path: 'asset/:id',
data: { networkSpecific: true },
component: AssetComponent
},
{
path: 'group/:id',
data: { networkSpecific: true },
component: AssetGroupComponent
},
{
path: '**',
redirectTo: 'all'
}
]
},
{
path: 'docs',
loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule),
data: { preload: true },
},
{
path: 'api',
loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule)
},
],
},
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class LiquidRoutingModule { }
@NgModule({
imports: [
CommonModule,
LiquidRoutingModule,
SharedModule,
],
declarations: [
LiquidMasterPageComponent,
]
})
export class LiquidMasterPageModule { }

View File

@ -0,0 +1,120 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { MasterPageComponent } from './components/master-page/master-page.component';
import { SharedModule } from './shared/shared.module';
import { StartComponent } from './components/start/start.component';
import { AddressComponent } from './components/address/address.component';
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
import { BlocksList } from './components/blocks-list/blocks-list.component';
import { RbfList } from './components/rbf-list/rbf-list.component';
const browserWindow = window || {};
// @ts-ignore
const browserWindowEnv = browserWindow.__env || {};
const routes: Routes = [
{
path: '',
component: MasterPageComponent,
children: [
{
path: 'mining/blocks',
redirectTo: 'blocks',
pathMatch: 'full'
},
{
path: 'tx/push',
component: PushTransactionComponent,
},
{
path: 'about',
loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule),
},
{
path: 'blocks',
component: BlocksList,
},
{
path: 'rbf',
component: RbfList,
},
{
path: 'terms-of-service',
loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule),
},
{
path: 'privacy-policy',
loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule),
},
{
path: 'trademark-policy',
loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule),
},
{
path: 'address/:id',
children: [],
component: AddressComponent,
data: {
ogImage: true,
networkSpecific: true,
}
},
{
path: 'tx',
component: StartComponent,
data: { preload: true, networkSpecific: true },
loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule),
},
{
path: 'block',
component: StartComponent,
data: { preload: true, networkSpecific: true },
loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule),
},
{
path: 'docs',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule),
data: { preload: true },
},
{
path: 'api',
loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule)
},
{
path: 'lightning',
loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule),
data: { preload: browserWindowEnv && browserWindowEnv.LIGHTNING === true, networks: ['bitcoin'] },
},
],
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
]
})
export class MasterPageRoutingModule { }
@NgModule({
imports: [
CommonModule,
MasterPageRoutingModule,
SharedModule,
],
declarations: [
MasterPageComponent,
]
})
export class MasterPageModule { }

View File

@ -9,6 +9,7 @@ import { BlockPreviewComponent } from './components/block/block-preview.componen
import { AddressPreviewComponent } from './components/address/address-preview.component'; import { AddressPreviewComponent } from './components/address/address-preview.component';
import { PoolPreviewComponent } from './components/pool/pool-preview.component'; import { PoolPreviewComponent } from './components/pool/pool-preview.component';
import { MasterPagePreviewComponent } from './components/master-page-preview/master-page-preview.component'; import { MasterPagePreviewComponent } from './components/master-page-preview/master-page-preview.component';
import { TxBowtieModule } from './components/tx-bowtie-graph/tx-bowtie.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
TransactionPreviewComponent, TransactionPreviewComponent,
@ -23,6 +24,7 @@ import { MasterPagePreviewComponent } from './components/master-page-preview/mas
RouterModule, RouterModule,
PreviewsRoutingModule, PreviewsRoutingModule,
GraphsModule, GraphsModule,
TxBowtieModule,
], ],
}) })
export class PreviewsModule { } export class PreviewsModule { }

View File

@ -31,7 +31,8 @@ const routes: Routes = [
}, },
{ {
path: 'lightning', path: 'lightning',
loadChildren: () => import('./lightning/lightning-previews.module').then(m => m.LightningPreviewsModule) loadChildren: () => import('./lightning/lightning-previews.module').then(m => m.LightningPreviewsModule),
data: { preload: true },
}, },
], ],
} }

View File

@ -84,6 +84,7 @@
<a href="https://github.com/mempool" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a> <a href="https://github.com/mempool" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a>
<a href="https://twitter.com/mempool" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a> <a href="https://twitter.com/mempool" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a>
<a href="nostr:npub18d4r6wanxkyrdfjdrjqzj2ukua5cas669ew2g5w7lf4a8te7awzqey6lt3" target="_blank"><svg fill="#fff" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 875 875"><path d="M684.72 485.57c.22 12.59-11.93 51.47-38.67 81.3-26.74 29.83-56.02 20.85-58.42 20.16s-3.09-4.46-7.89-3.77-9.6 6.17-18.86 7.2-17.49 1.71-26.06-1.37c-4.46.69-5.14.71-7.2 2.24s-17.83 10.79-21.6 11.47c0 7.2-1.37 44.57 0 55.89s3.77 25.71 7.54 36 2.74 10.63 7.54 9.94 13.37.34 15.77 4.11c2.4 3.77 1.37 6.51 5.49 8.23s60.69 17.14 99.43 19.2c26.74.69 42.86 2.74 52.12 19.54 1.37 7.89 7.54 13.03 11.31 14.06s8.23 2.06 12 5.83 1.03 8.23 5.49 11.66c4.46 3.43 14.74 8.57 25.37 13.71 10.63 5.14 15.09 13.37 15.77 16.11s1.71 10.97 1.71 10.97-8.91 0-10.97-2.06-2.74-5.83-2.74-5.83-6.17 1.03-7.54 3.43.69 2.74-7.89.69-11.66-3.77-18.17-8.57c-6.51-4.8-16.46-17.14-25.03-16.8 4.11 8.23 5.83 8.23 10.63 10.97s8.23 5.83 8.23 5.83l-7.2 4.46s-4.46 2.06-14.74-.69-11.66-4.46-12.69-10.63 0-9.26-2.74-14.4-4.11-15.77-22.29-21.26c-18.17-5.49-66.52-21.26-100.12-24.69s-22.63-2.74-28.11-1.37-15.77 4.46-26.4-1.37c-10.63-5.83-16.8-13.71-17.49-20.23s-1.71-10.97 0-19.2 3.43-19.89 1.71-26.74-14.06-55.89-19.89-64.12c-13.03 1.03-50.74-.69-50.74-.69s-2.4-.69-17.49 5.83-36.48 13.76-46.77 19.93-14.4 9.7-16.12 13.13c.12 3-1.23 7.72-2.79 9.06s-12.48 2.42-12.48 2.42-5.85 5.86-8.25 9.97c-6.86 9.6-55.2 125.14-66.52 149.83-13.54 32.57-9.77 27.43-37.71 27.43s-8.06.3-8.06.3-12.34 5.88-16.8 5.88-18.86-2.4-26.4 0-16.46 9.26-23.31 10.29-4.95-1.34-8.38-3.74c-4-.21-14.27-.12-14.27-.12s1.74-6.51 7.91-10.88c8.23-5.83 25.37-16.11 34.63-21.26s17.49-7.89 23.31-9.26 18.51-6.17 30.51-9.94 19.54-8.23 29.83-31.54 50.4-111.43 51.43-116.23c.63-2.96 3.73-6.48 4.8-15.09.66-5.35-2.49-13.04 1.71-22.63 10.97-25.03 21.6-20.23 26.4-20.23s17.14.34 26.4-1.37 15.43-2.74 24.69-7.89 11.31-8.91 11.31-8.91l-19.89-3.43s-18.51.69-25.03-4.46-15.43-15.77-15.43-15.77l-7.54-7.2 1.03 8.57s-5.14-8.91-6.51-10.29-8.57-6.51-11.31-11.31-7.54-25.03-7.54-25.03l-6.17 13.03-1.71-18.86-5.14 7.2-2.74-16.11-4.8 8.23-3.43-14.4-5.83 4.46-2.4-10.29-5.83-3.43s-14.06-9.26-16.46-9.6-4.46 3.43-4.46 3.43l1.37 12-12.2-6.27-7-11.9s2.36 4.01-9.62 7.53c-20.55 0-21.89-2.28-24.93-3.94-1.31-6.56-5.57-10.11-5.57-10.11h-20.57l-.34-6.86-7.89 3.09.69-10.29h-14.06l1.03-11.31h-8.91s3.09-9.26 25.71-22.97 25.03-16.46 46.29-17.14c21.26-.69 32.91 2.74 46.29 8.23s38.74 13.71 43.89 17.49c11.31-9.94 28.46-19.89 34.29-19.89 1.03-2.4 6.19-12.33 17.96-17.6 35.31-15.81 108.13-34 131.53-35.54 31.2-2.06 7.89-1.37 39.09 2.06 31.2 3.43 54.17 7.54 69.6 12.69 12.58 4.19 25.03 9.6 34.29 2.06 4.33-1.81 11.81-1.34 17.83-5.14 30.69-25.09 34.72-32.35 43.63-41.95s20.14-24.91 22.54-45.14 4.46-58.29-10.63-88.12-28.8-45.26-34.63-69.26c-5.83-24-8.23-61.03-6.17-73.03 2.06-12 5.14-22.29 6.86-30.51s9.94-14.74 19.89-16.46c9.94-1.71 17.83 1.37 22.29 4.8 4.46 3.43 11.65 6.28 13.37 10.29.34 1.71-1.37 6.51 8.23 8.23 9.6 1.71 16.05 4.16 16.05 4.16s15.64 4.29 3.11 7.73c-12.69 2.06-20.52-.71-24.29 1.69s-7.21 10.08-9.61 11.1-7.2.34-12 4.11-9.6 6.86-12.69 14.4-5.49 15.77-3.43 26.74 8.57 31.54 14.4 43.2c5.83 11.66 20.23 40.8 24.34 47.66s15.77 29.49 16.8 53.83 1.03 44.23 0 54.86-10.84 51.65-35.53 85.94c-8.16 14.14-23.21 31.9-24.67 35.03-1.45 3.13-3.02 4.88-1.61 7.65 4.62 9.05 12.87 22.13 14.71 29.22 2.29 6.64 6.99 16.13 7.22 28.72Z" style="stroke:#000;stroke-miterlimit:10;stroke-width:6px"/></svg></a> <a href="nostr:npub18d4r6wanxkyrdfjdrjqzj2ukua5cas669ew2g5w7lf4a8te7awzqey6lt3" target="_blank"><svg fill="#fff" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 875 875"><path d="M684.72 485.57c.22 12.59-11.93 51.47-38.67 81.3-26.74 29.83-56.02 20.85-58.42 20.16s-3.09-4.46-7.89-3.77-9.6 6.17-18.86 7.2-17.49 1.71-26.06-1.37c-4.46.69-5.14.71-7.2 2.24s-17.83 10.79-21.6 11.47c0 7.2-1.37 44.57 0 55.89s3.77 25.71 7.54 36 2.74 10.63 7.54 9.94 13.37.34 15.77 4.11c2.4 3.77 1.37 6.51 5.49 8.23s60.69 17.14 99.43 19.2c26.74.69 42.86 2.74 52.12 19.54 1.37 7.89 7.54 13.03 11.31 14.06s8.23 2.06 12 5.83 1.03 8.23 5.49 11.66c4.46 3.43 14.74 8.57 25.37 13.71 10.63 5.14 15.09 13.37 15.77 16.11s1.71 10.97 1.71 10.97-8.91 0-10.97-2.06-2.74-5.83-2.74-5.83-6.17 1.03-7.54 3.43.69 2.74-7.89.69-11.66-3.77-18.17-8.57c-6.51-4.8-16.46-17.14-25.03-16.8 4.11 8.23 5.83 8.23 10.63 10.97s8.23 5.83 8.23 5.83l-7.2 4.46s-4.46 2.06-14.74-.69-11.66-4.46-12.69-10.63 0-9.26-2.74-14.4-4.11-15.77-22.29-21.26c-18.17-5.49-66.52-21.26-100.12-24.69s-22.63-2.74-28.11-1.37-15.77 4.46-26.4-1.37c-10.63-5.83-16.8-13.71-17.49-20.23s-1.71-10.97 0-19.2 3.43-19.89 1.71-26.74-14.06-55.89-19.89-64.12c-13.03 1.03-50.74-.69-50.74-.69s-2.4-.69-17.49 5.83-36.48 13.76-46.77 19.93-14.4 9.7-16.12 13.13c.12 3-1.23 7.72-2.79 9.06s-12.48 2.42-12.48 2.42-5.85 5.86-8.25 9.97c-6.86 9.6-55.2 125.14-66.52 149.83-13.54 32.57-9.77 27.43-37.71 27.43s-8.06.3-8.06.3-12.34 5.88-16.8 5.88-18.86-2.4-26.4 0-16.46 9.26-23.31 10.29-4.95-1.34-8.38-3.74c-4-.21-14.27-.12-14.27-.12s1.74-6.51 7.91-10.88c8.23-5.83 25.37-16.11 34.63-21.26s17.49-7.89 23.31-9.26 18.51-6.17 30.51-9.94 19.54-8.23 29.83-31.54 50.4-111.43 51.43-116.23c.63-2.96 3.73-6.48 4.8-15.09.66-5.35-2.49-13.04 1.71-22.63 10.97-25.03 21.6-20.23 26.4-20.23s17.14.34 26.4-1.37 15.43-2.74 24.69-7.89 11.31-8.91 11.31-8.91l-19.89-3.43s-18.51.69-25.03-4.46-15.43-15.77-15.43-15.77l-7.54-7.2 1.03 8.57s-5.14-8.91-6.51-10.29-8.57-6.51-11.31-11.31-7.54-25.03-7.54-25.03l-6.17 13.03-1.71-18.86-5.14 7.2-2.74-16.11-4.8 8.23-3.43-14.4-5.83 4.46-2.4-10.29-5.83-3.43s-14.06-9.26-16.46-9.6-4.46 3.43-4.46 3.43l1.37 12-12.2-6.27-7-11.9s2.36 4.01-9.62 7.53c-20.55 0-21.89-2.28-24.93-3.94-1.31-6.56-5.57-10.11-5.57-10.11h-20.57l-.34-6.86-7.89 3.09.69-10.29h-14.06l1.03-11.31h-8.91s3.09-9.26 25.71-22.97 25.03-16.46 46.29-17.14c21.26-.69 32.91 2.74 46.29 8.23s38.74 13.71 43.89 17.49c11.31-9.94 28.46-19.89 34.29-19.89 1.03-2.4 6.19-12.33 17.96-17.6 35.31-15.81 108.13-34 131.53-35.54 31.2-2.06 7.89-1.37 39.09 2.06 31.2 3.43 54.17 7.54 69.6 12.69 12.58 4.19 25.03 9.6 34.29 2.06 4.33-1.81 11.81-1.34 17.83-5.14 30.69-25.09 34.72-32.35 43.63-41.95s20.14-24.91 22.54-45.14 4.46-58.29-10.63-88.12-28.8-45.26-34.63-69.26c-5.83-24-8.23-61.03-6.17-73.03 2.06-12 5.14-22.29 6.86-30.51s9.94-14.74 19.89-16.46c9.94-1.71 17.83 1.37 22.29 4.8 4.46 3.43 11.65 6.28 13.37 10.29.34 1.71-1.37 6.51 8.23 8.23 9.6 1.71 16.05 4.16 16.05 4.16s15.64 4.29 3.11 7.73c-12.69 2.06-20.52-.71-24.29 1.69s-7.21 10.08-9.61 11.1-7.2.34-12 4.11-9.6 6.86-12.69 14.4-5.49 15.77-3.43 26.74 8.57 31.54 14.4 43.2c5.83 11.66 20.23 40.8 24.34 47.66s15.77 29.49 16.8 53.83 1.03 44.23 0 54.86-10.84 51.65-35.53 85.94c-8.16 14.14-23.21 31.9-24.67 35.03-1.45 3.13-3.02 4.88-1.61 7.65 4.62 9.05 12.87 22.13 14.71 29.22 2.29 6.64 6.99 16.13 7.22 28.72Z" style="stroke:#000;stroke-miterlimit:10;stroke-width:6px"/></svg></a>
<a href="https://primal.net/mempool" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg" ><path d="m155.5 253c-8.9 2-18.1 3-27.5 3-25.9 0-50-7.7-70.2-20.9-5-7.2-7.2-11.1-8.9-14-0.8-1.4-1.5-2.6-2.2-3.8-7.7-12.2-11.7-28-12.5-46.6-2.7-57.4 32.2-94 67.8-100.1 22.6-3.8 40.6 0.1 54.3 7.5-12.1-3.4-26.6-3.6-43.2 1.1-40.1 12.9-53.5 52.3-47.8 95.8 10 54.6 63.5 74.1 90.2 78zm-114.3-30.9c-7.4-13.2-14.2-33-15-51-2.9-60.9 34.4-101.6 74.5-108.3 54.7-9.3 85.1 23.1 95.6 47.1 0.4-0.3 0.6-0.9 0.3-1.4-17.2-37.4-52.8-63.2-94-63.2-46.8 0-88.5 33.6-102.6 83.4 0.2 36.9 16 70.2 41.2 93.4zm158.8 11.7c-9.2 6.3-19.3 11.5-30.1 15.2-5.1-0.9-10.9-2-14.9-2.8-1.9-0.4-3.4-0.7-4.3-0.9-24.4-4.4-67.9-20.1-77.5-71.6-2.6-20.6-0.7-39.7 6.1-54.8 6.7-14.9 18.4-26.3 36.1-32 20.6-5.6 37.7-2.9 50.3 3.9q-4.7-0.9-9.6-1c-27.4 0-49.7 23.9-49.7 53.3 0 11.7 3.6 22.5 9.6 31.3 0 0 17.2 32.5 64 29.6 41.7-2.6 63.4-40 66-53.8 1.3-7.2 2-14.6 2-22.2 0-66.3-53.7-120-120-120-50.1 0-93.1 30.8-111.1 74.4-6 7.9-11.2 16.7-15.4 26.2 9.3-61.5 62.4-108.6 126.5-108.6 70.7 0 128 57.3 128 128 0 44-22.2 82.8-56 105.8z" style="fill:#ffffff"/></svg></a>
<a href="https://youtube.com/@mempool" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>YouTube</title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg></a> <a href="https://youtube.com/@mempool" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>YouTube</title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg></a>
<a href="https://bitcointv.com/c/mempool/videos" target="_blank"><svg _ngcontent-serverApp-c90="" xmlns="http://www.w3.org/2000/svg" focusable="false" viewBox="0 0 440 440"><path _ngcontent-serverApp-c90="" d="M225.57,2.08l-.69-.45a4.22,4.22,0,0,0-5.72,1.23L182.33,46.09a4,4,0,0,0,.88,5.81l9.38,6.38L173.48,97.49a4.22,4.22,0,0,0,2.45,4.19s3.55.7,4.53-1l41.92-40.56a3.62,3.62,0,0,0-1.51-5.1l-10.55-6.12L227.44,6.79A4.26,4.26,0,0,0,225.57,2.08Z" fill="#fff"></path><path _ngcontent-serverApp-c90="" d="M118.52,401.83c-62.51,0-113.37-51-113.37-113.67V214.68C5.15,152,56,101,118.52,101H342.08a24.82,24.82,0,0,1,24.76,24.83V377a24.81,24.81,0,0,1-24.76,24.82Z"></path><path _ngcontent-serverApp-c90="" d="M342.08,105.18a20.65,20.65,0,0,1,20.61,20.66V377a20.66,20.66,0,0,1-20.61,20.66H118.52C58.3,397.67,9.31,348.55,9.31,288.16V214.68c0-60.38,49-109.5,109.21-109.5H342.08m0-8.34H118.52C53.62,96.84,1,149.6,1,214.68v73.48C1,353.24,53.62,406,118.52,406H342.08A29,29,0,0,0,371,377V125.84a29,29,0,0,0-28.92-29Z" fill="#fff"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M344.69,346.23A25.84,25.84,0,1,0,335,369.87l-10.22-10.2a11.69,11.69,0,1,1,4.77-5.12l10.31,10.28A25.84,25.84,0,0,0,344.69,346.23Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M315.82,257.61a25.67,25.67,0,0,0-12.53,5.22L315,274.49a9.58,9.58,0,0,1,2.11-.73A9.72,9.72,0,1,1,309.4,283a9.4,9.4,0,0,1,.75-3.41L298.4,267.84a25.77,25.77,0,1,0,17.42-10.23Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M313,214a7.76,7.76,0,1,1,1.41,10.91,7.62,7.62,0,0,1-2.19-2.69l-18.67-.14a25.94,25.94,0,1,0,.05-7l18.64.14A7.4,7.4,0,0,1,313,214Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M341.2,144.08h-6.32c-1.67,0-3.61,1.87-3.61,4.29s1.94,4.29,3.61,4.29h6.32c1.67,0,3.61-1.87,3.61-4.29S342.87,144.08,341.2,144.08Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M301.75,144.08h-6.44c-1.67,0-3.61,1.87-3.61,4.29s1.94,4.29,3.61,4.29h6.44c1.67,0,3.61-1.87,3.61-4.29S303.42,144.08,301.75,144.08Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M321.77,144.08h-7c-1.67,0-3.62,1.87-3.62,4.29s1.95,4.29,3.62,4.29h7c1.67,0,3.62-1.87,3.62-4.29S323.44,144.08,321.77,144.08Z"></path><ellipse _ngcontent-serverApp-c90="" fill="#fff" cx="295.97" cy="127.61" rx="4.27" ry="4.29"></ellipse><path _ngcontent-serverApp-c90="" fill="#fff" d="M340.54,131.9a4.29,4.29,0,1,0-4.27-4.29A4.28,4.28,0,0,0,340.54,131.9Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M318.26,131.9a4.29,4.29,0,1,0-4.27-4.29A4.29,4.29,0,0,0,318.26,131.9Z"></path><ellipse _ngcontent-serverApp-c90="" fill="#fff" cx="295.97" cy="169.13" rx="4.27" ry="4.29"></ellipse><path _ngcontent-serverApp-c90="" fill="#fff" d="M340.54,164.84a4.3,4.3,0,1,0,4.27,4.29A4.29,4.29,0,0,0,340.54,164.84Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M318.26,164.84a4.3,4.3,0,1,0,4.28,4.29A4.29,4.29,0,0,0,318.26,164.84Z"></path><path _ngcontent-serverApp-c90="" d="M108.62,256.87c8.36-1,7.68-7.76,3.14-17-3.64-7.4-9.74-16.39-15.75-25.36-14.23-21.23-27.69-42.23-5.35-41.07,19.55,1,42.9,18.63,68.22,36.74,31.1,22.24,65.16,45.21,98.81,39.11a151.19,151.19,0,0,1,20-2.37V221a92,92,0,0,0-91.91-92.16H124.33A92,92,0,0,0,32.42,221v17.59c17.71,3.81,31,9.94,43.8,14.15C86.6,256.16,96.69,258.31,108.62,256.87Z"></path><path _ngcontent-serverApp-c90="" d="M273.37,310.79c-35-15.26-76.67-32.1-104-23.59-3.15,1-5,2.3-6,3.85-3.35,5.31,4.67,13.57,14.89,22.17,7.17,6,15.36,12.21,21.44,17.64,11.47,10.26,15.35,17.84-9.89,16.62-29.75-1.44-49.18-13.75-71.18-24l-.29-.14a165.84,165.84,0,0,0-22.93-8.91c-15.74-4.67-34.22-6.79-58.51-3.28A91.93,91.93,0,0,0,124.33,375h61.45A92,92,0,0,0,273.37,310.79Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M257.69,249.31C224,255.41,190,232.44,158.88,210.2c-25.32-18.11-48.67-35.72-68.22-36.74C68.32,172.3,81.78,193.3,96,214.53c6,9,12.11,18,15.75,25.36,4.54,9.22,5.22,16-3.14,17-11.93,1.44-22-.71-32.4-4.13-12.8-4.21-26.09-10.34-43.8-14.15v44.26c0,1.26.14,2.48.19,3.72a91.8,91.8,0,0,0,2.9,19.62c.43,1.67.84,3.34,1.37,5,24.29-3.51,42.77-1.39,58.51,3.28a165.84,165.84,0,0,1,22.93,8.91c.39-.12.76-.26,1.14-.39l-.85.53c22,10.25,41.43,22.56,71.18,24,25.24,1.22,21.36-6.36,9.89-16.62-6.08-5.43-14.27-11.61-21.44-17.64-10.22-8.6-18.24-16.86-14.89-22.17,1-1.55,2.87-2.87,6-3.85,27.33-8.51,69,8.33,104,23.59.32-1,.56-2.05.84-3.07a92.33,92.33,0,0,0,3.48-24.87V246.94A151.19,151.19,0,0,0,257.69,249.31Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M192,137a78,78,0,0,1,77.78,78v73.91a78,78,0,0,1-77.78,78H118.51a78,78,0,0,1-77.78-78V215a78,78,0,0,1,77.78-78H192m0-8.33H118.51A86.21,86.21,0,0,0,32.42,215v73.91a86.21,86.21,0,0,0,86.09,86.33H192a86.21,86.21,0,0,0,86.09-86.33V215A86.21,86.21,0,0,0,192,128.64Z"></path></svg></a> <a href="https://bitcointv.com/c/mempool/videos" target="_blank"><svg _ngcontent-serverApp-c90="" xmlns="http://www.w3.org/2000/svg" focusable="false" viewBox="0 0 440 440"><path _ngcontent-serverApp-c90="" d="M225.57,2.08l-.69-.45a4.22,4.22,0,0,0-5.72,1.23L182.33,46.09a4,4,0,0,0,.88,5.81l9.38,6.38L173.48,97.49a4.22,4.22,0,0,0,2.45,4.19s3.55.7,4.53-1l41.92-40.56a3.62,3.62,0,0,0-1.51-5.1l-10.55-6.12L227.44,6.79A4.26,4.26,0,0,0,225.57,2.08Z" fill="#fff"></path><path _ngcontent-serverApp-c90="" d="M118.52,401.83c-62.51,0-113.37-51-113.37-113.67V214.68C5.15,152,56,101,118.52,101H342.08a24.82,24.82,0,0,1,24.76,24.83V377a24.81,24.81,0,0,1-24.76,24.82Z"></path><path _ngcontent-serverApp-c90="" d="M342.08,105.18a20.65,20.65,0,0,1,20.61,20.66V377a20.66,20.66,0,0,1-20.61,20.66H118.52C58.3,397.67,9.31,348.55,9.31,288.16V214.68c0-60.38,49-109.5,109.21-109.5H342.08m0-8.34H118.52C53.62,96.84,1,149.6,1,214.68v73.48C1,353.24,53.62,406,118.52,406H342.08A29,29,0,0,0,371,377V125.84a29,29,0,0,0-28.92-29Z" fill="#fff"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M344.69,346.23A25.84,25.84,0,1,0,335,369.87l-10.22-10.2a11.69,11.69,0,1,1,4.77-5.12l10.31,10.28A25.84,25.84,0,0,0,344.69,346.23Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M315.82,257.61a25.67,25.67,0,0,0-12.53,5.22L315,274.49a9.58,9.58,0,0,1,2.11-.73A9.72,9.72,0,1,1,309.4,283a9.4,9.4,0,0,1,.75-3.41L298.4,267.84a25.77,25.77,0,1,0,17.42-10.23Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M313,214a7.76,7.76,0,1,1,1.41,10.91,7.62,7.62,0,0,1-2.19-2.69l-18.67-.14a25.94,25.94,0,1,0,.05-7l18.64.14A7.4,7.4,0,0,1,313,214Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M341.2,144.08h-6.32c-1.67,0-3.61,1.87-3.61,4.29s1.94,4.29,3.61,4.29h6.32c1.67,0,3.61-1.87,3.61-4.29S342.87,144.08,341.2,144.08Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M301.75,144.08h-6.44c-1.67,0-3.61,1.87-3.61,4.29s1.94,4.29,3.61,4.29h6.44c1.67,0,3.61-1.87,3.61-4.29S303.42,144.08,301.75,144.08Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M321.77,144.08h-7c-1.67,0-3.62,1.87-3.62,4.29s1.95,4.29,3.62,4.29h7c1.67,0,3.62-1.87,3.62-4.29S323.44,144.08,321.77,144.08Z"></path><ellipse _ngcontent-serverApp-c90="" fill="#fff" cx="295.97" cy="127.61" rx="4.27" ry="4.29"></ellipse><path _ngcontent-serverApp-c90="" fill="#fff" d="M340.54,131.9a4.29,4.29,0,1,0-4.27-4.29A4.28,4.28,0,0,0,340.54,131.9Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M318.26,131.9a4.29,4.29,0,1,0-4.27-4.29A4.29,4.29,0,0,0,318.26,131.9Z"></path><ellipse _ngcontent-serverApp-c90="" fill="#fff" cx="295.97" cy="169.13" rx="4.27" ry="4.29"></ellipse><path _ngcontent-serverApp-c90="" fill="#fff" d="M340.54,164.84a4.3,4.3,0,1,0,4.27,4.29A4.29,4.29,0,0,0,340.54,164.84Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M318.26,164.84a4.3,4.3,0,1,0,4.28,4.29A4.29,4.29,0,0,0,318.26,164.84Z"></path><path _ngcontent-serverApp-c90="" d="M108.62,256.87c8.36-1,7.68-7.76,3.14-17-3.64-7.4-9.74-16.39-15.75-25.36-14.23-21.23-27.69-42.23-5.35-41.07,19.55,1,42.9,18.63,68.22,36.74,31.1,22.24,65.16,45.21,98.81,39.11a151.19,151.19,0,0,1,20-2.37V221a92,92,0,0,0-91.91-92.16H124.33A92,92,0,0,0,32.42,221v17.59c17.71,3.81,31,9.94,43.8,14.15C86.6,256.16,96.69,258.31,108.62,256.87Z"></path><path _ngcontent-serverApp-c90="" d="M273.37,310.79c-35-15.26-76.67-32.1-104-23.59-3.15,1-5,2.3-6,3.85-3.35,5.31,4.67,13.57,14.89,22.17,7.17,6,15.36,12.21,21.44,17.64,11.47,10.26,15.35,17.84-9.89,16.62-29.75-1.44-49.18-13.75-71.18-24l-.29-.14a165.84,165.84,0,0,0-22.93-8.91c-15.74-4.67-34.22-6.79-58.51-3.28A91.93,91.93,0,0,0,124.33,375h61.45A92,92,0,0,0,273.37,310.79Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M257.69,249.31C224,255.41,190,232.44,158.88,210.2c-25.32-18.11-48.67-35.72-68.22-36.74C68.32,172.3,81.78,193.3,96,214.53c6,9,12.11,18,15.75,25.36,4.54,9.22,5.22,16-3.14,17-11.93,1.44-22-.71-32.4-4.13-12.8-4.21-26.09-10.34-43.8-14.15v44.26c0,1.26.14,2.48.19,3.72a91.8,91.8,0,0,0,2.9,19.62c.43,1.67.84,3.34,1.37,5,24.29-3.51,42.77-1.39,58.51,3.28a165.84,165.84,0,0,1,22.93,8.91c.39-.12.76-.26,1.14-.39l-.85.53c22,10.25,41.43,22.56,71.18,24,25.24,1.22,21.36-6.36,9.89-16.62-6.08-5.43-14.27-11.61-21.44-17.64-10.22-8.6-18.24-16.86-14.89-22.17,1-1.55,2.87-2.87,6-3.85,27.33-8.51,69,8.33,104,23.59.32-1,.56-2.05.84-3.07a92.33,92.33,0,0,0,3.48-24.87V246.94A151.19,151.19,0,0,0,257.69,249.31Z"></path><path _ngcontent-serverApp-c90="" fill="#fff" d="M192,137a78,78,0,0,1,77.78,78v73.91a78,78,0,0,1-77.78,78H118.51a78,78,0,0,1-77.78-78V215a78,78,0,0,1,77.78-78H192m0-8.33H118.51A86.21,86.21,0,0,0,32.42,215v73.91a86.21,86.21,0,0,0,86.09,86.33H192a86.21,86.21,0,0,0,86.09-86.33V215A86.21,86.21,0,0,0,192,128.64Z"></path></svg></a>
<a href="https://mempool.chat" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Matrix</title><path d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.481.314.448.208.785.582 1.02 1.108.254-.374.6-.706 1.034-.992.434-.287.95-.43 1.546-.43.453 0 .872.056 1.26.167.388.11.716.286.993.53.276.245.489.559.646.951.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66 1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499 1.946 1.946 0 0 0-.231.696 5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688 1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19-.111 0-.259.024-.439.074-.18.051-.36.143-.53.282-.171.138-.319.337-.439.595-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg></a> <a href="https://mempool.chat" target="_blank"><svg fill="#fff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Matrix</title><path d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.481.314.448.208.785.582 1.02 1.108.254-.374.6-.706 1.034-.992.434-.287.95-.43 1.546-.43.453 0 .872.056 1.26.167.388.11.716.286.993.53.276.245.489.559.646.951.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66 1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499 1.946 1.946 0 0 0-.231.696 5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688 1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19-.111 0-.259.024-.439.074-.18.051-.36.143-.53.282-.171.138-.319.337-.439.595-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg></a>

View File

@ -88,7 +88,14 @@ footer .row.link-tree {
footer .row.social-links { footer .row.social-links {
text-align: center; text-align: center;
margin: 24px 0; display: flex;
flex-wrap: wrap;
width: fit-content;
margin: 0 auto;
@media (max-width: 450px){
width: 250px;
}
} }
footer .row.social-links a { footer .row.social-links a {
@ -97,6 +104,7 @@ footer .row.social-links a {
footer .row.social-links svg { footer .row.social-links svg {
width: 20px; width: 20px;
margin: 10px 0 10px 0;
} }
footer .row.version { footer .row.version {
@ -189,10 +197,6 @@ footer .sponsor {
margin-top: 15px; margin-top: 15px;
} }
footer .row.social-links {
margin: 48px 0 24px 0;
}
footer .selector:not(:last-child) { footer .selector:not(:last-child) {
margin-right: 10px; margin-right: 10px;
} }
@ -236,10 +240,6 @@ footer .sponsor {
margin-top: 15px; margin-top: 15px;
} }
footer .services.row.social-links {
margin: 48px 0 24px 0;
}
footer .services.selector:not(:last-child) { footer .services.selector:not(:last-child) {
margin-right: 10px; margin-right: 10px;
} }

View File

@ -6,12 +6,8 @@ import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, fa
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle } from '@fortawesome/free-solid-svg-icons'; faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle } from '@fortawesome/free-solid-svg-icons';
import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { MasterPageComponent } from '../components/master-page/master-page.component';
import { MenuComponent } from '../components/menu/menu.component'; import { MenuComponent } from '../components/menu/menu.component';
import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component'; import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component';
import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component';
import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component';
import { AboutComponent } from '../components/about/about.component';
import { VbytesPipe } from './pipes/bytes-pipe/vbytes.pipe'; import { VbytesPipe } from './pipes/bytes-pipe/vbytes.pipe';
import { ShortenStringPipe } from './pipes/shorten-string-pipe/shorten-string.pipe'; import { ShortenStringPipe } from './pipes/shorten-string-pipe/shorten-string.pipe';
import { CeilPipe } from './pipes/math-ceil/math-ceil.pipe'; import { CeilPipe } from './pipes/math-ceil/math-ceil.pipe';
@ -45,9 +41,7 @@ import { AmountComponent } from '../components/amount/amount.component';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { CapAddressPipe } from './pipes/cap-address-pipe/cap-address-pipe'; import { CapAddressPipe } from './pipes/cap-address-pipe/cap-address-pipe';
import { StartComponent } from '../components/start/start.component'; import { StartComponent } from '../components/start/start.component';
import { TransactionComponent } from '../components/transaction/transaction.component';
import { TransactionsListComponent } from '../components/transactions-list/transactions-list.component'; import { TransactionsListComponent } from '../components/transactions-list/transactions-list.component';
import { BlockComponent } from '../components/block/block.component';
import { BlockOverviewGraphComponent } from '../components/block-overview-graph/block-overview-graph.component'; import { BlockOverviewGraphComponent } from '../components/block-overview-graph/block-overview-graph.component';
import { BlockOverviewTooltipComponent } from '../components/block-overview-tooltip/block-overview-tooltip.component'; import { BlockOverviewTooltipComponent } from '../components/block-overview-tooltip/block-overview-tooltip.component';
import { AddressComponent } from '../components/address/address.component'; import { AddressComponent } from '../components/address/address.component';
@ -62,13 +56,8 @@ import { FeesBoxComponent } from '../components/fees-box/fees-box.component';
import { DifficultyComponent } from '../components/difficulty/difficulty.component'; import { DifficultyComponent } from '../components/difficulty/difficulty.component';
import { DifficultyTooltipComponent } from '../components/difficulty/difficulty-tooltip.component'; import { DifficultyTooltipComponent } from '../components/difficulty/difficulty-tooltip.component';
import { DifficultyMiningComponent } from '../components/difficulty-mining/difficulty-mining.component'; import { DifficultyMiningComponent } from '../components/difficulty-mining/difficulty-mining.component';
import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component';
import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component'; import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component';
import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component'; import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component';
import { TxBowtieGraphComponent } from '../components/tx-bowtie-graph/tx-bowtie-graph.component';
import { TxBowtieGraphTooltipComponent } from '../components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component';
import { PrivacyPolicyComponent } from '../components/privacy-policy/privacy-policy.component';
import { TrademarkPolicyComponent } from '../components/trademark-policy/trademark-policy.component';
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component'; import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component';
import { AssetGroupComponent } from '../components/assets/asset-group/asset-group.component'; import { AssetGroupComponent } from '../components/assets/asset-group/asset-group.component';
@ -97,6 +86,8 @@ import { AcceleratePreviewComponent } from '../components/accelerate-preview/acc
import { AccelerateFeeGraphComponent } from '../components/accelerate-preview/accelerate-fee-graph.component'; import { AccelerateFeeGraphComponent } from '../components/accelerate-preview/accelerate-fee-graph.component';
import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component'; import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component';
import { BlockViewComponent } from '../components/block-view/block-view.component';
import { MempoolBlockViewComponent } from '../components/mempool-block-view/mempool-block-view.component';
import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component'; import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component';
import { ClockchainComponent } from '../components/clockchain/clockchain.component'; import { ClockchainComponent } from '../components/clockchain/clockchain.component';
import { ClockFaceComponent } from '../components/clock-face/clock-face.component'; import { ClockFaceComponent } from '../components/clock-face/clock-face.component';
@ -134,18 +125,14 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
FiatCurrencyPipe, FiatCurrencyPipe,
ColoredPriceDirective, ColoredPriceDirective,
BlockchainComponent, BlockchainComponent,
BlockViewComponent,
MempoolBlockViewComponent,
MempoolBlocksComponent, MempoolBlocksComponent,
BlockchainBlocksComponent, BlockchainBlocksComponent,
AmountComponent, AmountComponent,
AboutComponent,
MasterPageComponent,
MenuComponent, MenuComponent,
PreviewTitleComponent, PreviewTitleComponent,
BisqMasterPageComponent,
LiquidMasterPageComponent,
StartComponent, StartComponent,
TransactionComponent,
BlockComponent,
BlockOverviewGraphComponent, BlockOverviewGraphComponent,
BlockOverviewTooltipComponent, BlockOverviewTooltipComponent,
TransactionsListComponent, TransactionsListComponent,
@ -162,11 +149,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
DifficultyTooltipComponent, DifficultyTooltipComponent,
RbfTimelineComponent, RbfTimelineComponent,
RbfTimelineTooltipComponent, RbfTimelineTooltipComponent,
TxBowtieGraphComponent,
TxBowtieGraphTooltipComponent,
TermsOfServiceComponent,
PrivacyPolicyComponent,
TrademarkPolicyComponent,
PushTransactionComponent, PushTransactionComponent,
AssetsNavComponent, AssetsNavComponent,
AssetsFeaturedComponent, AssetsFeaturedComponent,
@ -196,6 +178,8 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
AccelerateFeeGraphComponent, AccelerateFeeGraphComponent,
CalculatorComponent, CalculatorComponent,
BitcoinsatoshisPipe, BitcoinsatoshisPipe,
BlockViewComponent,
MempoolBlockViewComponent,
MempoolBlockOverviewComponent, MempoolBlockOverviewComponent,
ClockchainComponent, ClockchainComponent,
ClockComponent, ClockComponent,
@ -227,7 +211,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
AmountShortenerPipe, AmountShortenerPipe,
], ],
exports: [ exports: [
MasterPageComponent,
MenuComponent, MenuComponent,
RouterModule, RouterModule,
ReactiveFormsModule, ReactiveFormsModule,
@ -269,8 +252,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
BlockchainBlocksComponent, BlockchainBlocksComponent,
AmountComponent, AmountComponent,
StartComponent, StartComponent,
TransactionComponent,
BlockComponent,
BlockOverviewGraphComponent, BlockOverviewGraphComponent,
BlockOverviewTooltipComponent, BlockOverviewTooltipComponent,
TransactionsListComponent, TransactionsListComponent,
@ -287,11 +268,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
DifficultyTooltipComponent, DifficultyTooltipComponent,
RbfTimelineComponent, RbfTimelineComponent,
RbfTimelineTooltipComponent, RbfTimelineTooltipComponent,
TxBowtieGraphComponent,
TxBowtieGraphTooltipComponent,
TermsOfServiceComponent,
PrivacyPolicyComponent,
TrademarkPolicyComponent,
PushTransactionComponent, PushTransactionComponent,
AssetsNavComponent, AssetsNavComponent,
AssetsFeaturedComponent, AssetsFeaturedComponent,
@ -314,6 +290,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
ConfirmationsComponent, ConfirmationsComponent,
ToggleComponent, ToggleComponent,
GeolocationComponent, GeolocationComponent,
TestnetAlertComponent,
PreviewTitleComponent, PreviewTitleComponent,
GlobalFooterComponent, GlobalFooterComponent,
AcceleratePreviewComponent, AcceleratePreviewComponent,

View File

@ -332,7 +332,7 @@ BITCOIN_REPO_URL=https://github.com/bitcoin/bitcoin
BITCOIN_REPO_NAME=bitcoin BITCOIN_REPO_NAME=bitcoin
BITCOIN_REPO_BRANCH=master BITCOIN_REPO_BRANCH=master
#BITCOIN_LATEST_RELEASE=$(curl -s https://api.github.com/repos/bitcoin/bitcoin/releases/latest|grep tag_name|head -1|cut -d '"' -f4) #BITCOIN_LATEST_RELEASE=$(curl -s https://api.github.com/repos/bitcoin/bitcoin/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
BITCOIN_LATEST_RELEASE=v25.0 BITCOIN_LATEST_RELEASE=v25.1
echo -n '.' echo -n '.'
BISQ_REPO_URL=https://github.com/bisq-network/bisq BISQ_REPO_URL=https://github.com/bisq-network/bisq
@ -1660,15 +1660,15 @@ case $OS in
crontab_bitcoin=() crontab_bitcoin=()
if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then
echo "[*] Installing Electrs Mainnet Cronjob" echo "[*] Installing Electrs Mainnet Cronjob"
crontab_bitcoin+="@reboot sleep 30 ; screen -dmS mainnet /bitcoin/electrs/electrs-start-mainnet\n" crontab_bitcoin+="@reboot sleep 30 ; screen -dmS mainnet /bitcoin/electrs/start mainnet\n"
fi fi
if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then
echo "[*] Installing Electrs Testnet Cronjob" echo "[*] Installing Electrs Testnet Cronjob"
crontab_bitcoin+="@reboot sleep 70 ; screen -dmS testnet /bitcoin/electrs/electrs-start-testnet\n" crontab_bitcoin+="@reboot sleep 70 ; screen -dmS testnet /bitcoin/electrs/start testnet\n"
fi fi
if [ "${BITCOIN_SIGNET_ENABLE}" = ON ];then if [ "${BITCOIN_SIGNET_ENABLE}" = ON ];then
echo "[*] Installing Electrs Signet Cronjob" echo "[*] Installing Electrs Signet Cronjob"
crontab_bitcoin+="@reboot sleep 90 ; screen -dmS signet /bitcoin/electrs/electrs-start-signet\n" crontab_bitcoin+="@reboot sleep 90 ; screen -dmS signet /bitcoin/electrs/start signet\n"
fi fi
if [ "${BITCOIN_MAINNET_ENABLE}" = ON -o "${BITCOIN_TESTNET_ENABLE}" = ON -o "${BITCOIN_SIGNET_ENABLE}" = ON ];then if [ "${BITCOIN_MAINNET_ENABLE}" = ON -o "${BITCOIN_TESTNET_ENABLE}" = ON -o "${BITCOIN_SIGNET_ENABLE}" = ON ];then
echo "${crontab_bitcoin}" | crontab -u "${BITCOIN_USER}" - echo "${crontab_bitcoin}" | crontab -u "${BITCOIN_USER}" -

View File

@ -1,4 +1,16 @@
const configFile = require('../config.json'); const fs = require('fs');
const path = require('path');
const configPath = process.env.UNFURLER_CONFIG || './config.json';
const absolutePath = path.resolve(configPath);
let config;
try {
config = JSON.parse(fs.readFileSync(absolutePath, 'utf8'));
} catch (e) {
console.error(`Could not read config file ${absolutePath}: ${e}`);
process.exit(-1);
}
interface IConfig { interface IConfig {
SERVER: { SERVER: {
@ -57,7 +69,7 @@ class Config implements IConfig {
SYSLOG: IConfig['SYSLOG']; SYSLOG: IConfig['SYSLOG'];
constructor() { constructor() {
const configs = this.merge(configFile, defaults); const configs = this.merge(config, defaults);
this.SERVER = configs.SERVER; this.SERVER = configs.SERVER;
this.MEMPOOL = configs.MEMPOOL; this.MEMPOOL = configs.MEMPOOL;
this.PUPPETEER = configs.PUPPETEER; this.PUPPETEER = configs.PUPPETEER;