Compare commits
	
		
			2 Commits
		
	
	
		
			master
			...
			translatio
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a62a3cc774 | ||
|  | 9660723e96 | 
							
								
								
									
										68
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @ -251,7 +251,17 @@ jobs: | |||||||
|     strategy: |     strategy: | ||||||
|       fail-fast: false |       fail-fast: false | ||||||
|       matrix: |       matrix: | ||||||
|         module: ["mempool", "liquid", "testnet4"] |         module: ["mempool", "liquid"] | ||||||
|  |         include: | ||||||
|  |           - module: "mempool" | ||||||
|  |             spec: | | ||||||
|  |               cypress/e2e/mainnet/*.spec.ts | ||||||
|  |               cypress/e2e/signet/*.spec.ts | ||||||
|  |               cypress/e2e/testnet4/*.spec.ts | ||||||
|  |           - module: "liquid" | ||||||
|  |             spec: | | ||||||
|  |               cypress/e2e/liquid/liquid.spec.ts | ||||||
|  |               cypress/e2e/liquidtestnet/liquidtestnet.spec.ts | ||||||
| 
 | 
 | ||||||
|     name: E2E tests for ${{ matrix.module }} |     name: E2E tests for ${{ matrix.module }} | ||||||
|     steps: |     steps: | ||||||
| @ -301,9 +311,7 @@ jobs: | |||||||
|       - name: Unzip assets before building (src/resources) |       - name: Unzip assets before building (src/resources) | ||||||
|         run: unzip -o promo-video-assets.zip -d ${{ matrix.module }}/frontend/src/resources/promo-video |         run: unzip -o promo-video-assets.zip -d ${{ matrix.module }}/frontend/src/resources/promo-video | ||||||
|        |        | ||||||
|       # mempool |  | ||||||
|       - name: Chrome browser tests (${{ matrix.module }}) |       - name: Chrome browser tests (${{ matrix.module }}) | ||||||
|         if: ${{ matrix.module == 'mempool' }} |  | ||||||
|         uses: cypress-io/github-action@v5 |         uses: cypress-io/github-action@v5 | ||||||
|         with: |         with: | ||||||
|           tag: ${{ github.event_name }} |           tag: ${{ github.event_name }} | ||||||
| @ -314,9 +322,7 @@ jobs: | |||||||
|           wait-on-timeout: 120 |           wait-on-timeout: 120 | ||||||
|           record: true |           record: true | ||||||
|           parallel: true |           parallel: true | ||||||
|           spec: | |           spec: ${{ matrix.spec }} | ||||||
|             cypress/e2e/mainnet/*.spec.ts |  | ||||||
|             cypress/e2e/signet/*.spec.ts |  | ||||||
|           group: Tests on Chrome (${{ matrix.module }}) |           group: Tests on Chrome (${{ matrix.module }}) | ||||||
|           browser: "chrome" |           browser: "chrome" | ||||||
|           ci-build-id: "${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}" |           ci-build-id: "${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}" | ||||||
| @ -326,56 +332,6 @@ jobs: | |||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||||
|           CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} |           CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} | ||||||
| 
 | 
 | ||||||
|       # liquid |  | ||||||
|       - name: Chrome browser tests (${{ matrix.module }}) |  | ||||||
|         if: ${{ matrix.module == 'liquid' }} |  | ||||||
|         uses: cypress-io/github-action@v5 |  | ||||||
|         with: |  | ||||||
|           tag: ${{ github.event_name }} |  | ||||||
|           working-directory: ${{ matrix.module }}/frontend |  | ||||||
|           build: npm run config:defaults:${{ matrix.module }} |  | ||||||
|           start: npm run start:local-staging |  | ||||||
|           wait-on: "http://localhost:4200" |  | ||||||
|           wait-on-timeout: 120 |  | ||||||
|           record: true |  | ||||||
|           parallel: true |  | ||||||
|           spec: | |  | ||||||
|             cypress/e2e/liquid/liquid.spec.ts |  | ||||||
|             cypress/e2e/liquidtestnet/liquidtestnet.spec.ts |  | ||||||
|           group: Tests on Chrome (${{ matrix.module }}) |  | ||||||
|           browser: "chrome" |  | ||||||
|           ci-build-id: "${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}" |  | ||||||
|         env: |  | ||||||
|           COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} |  | ||||||
|           CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |  | ||||||
|           CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} |  | ||||||
| 
 |  | ||||||
|       # testnet |  | ||||||
|       - name: Chrome browser tests (${{ matrix.module }}) |  | ||||||
|         if: ${{ matrix.module == 'testnet4' }} |  | ||||||
|         uses: cypress-io/github-action@v5 |  | ||||||
|         with: |  | ||||||
|           tag: ${{ github.event_name }} |  | ||||||
|           working-directory: ${{ matrix.module }}/frontend |  | ||||||
|           build: npm run config:defaults:mempool |  | ||||||
|           start: npm run start:local-staging |  | ||||||
|           wait-on: "http://localhost:4200" |  | ||||||
|           wait-on-timeout: 120 |  | ||||||
|           record: true |  | ||||||
|           parallel: true |  | ||||||
|           spec: | |  | ||||||
|             cypress/e2e/testnet4/*.spec.ts |  | ||||||
|           group: Tests on Chrome (${{ matrix.module }}) |  | ||||||
|           browser: "chrome" |  | ||||||
|           ci-build-id: "${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}" |  | ||||||
|         env: |  | ||||||
|           CYPRESS_REROUTE_TESTNET: true |  | ||||||
|           COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} |  | ||||||
|           CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |  | ||||||
|           CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} |  | ||||||
| 
 |  | ||||||
|   validate_docker_json: |   validate_docker_json: | ||||||
|     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/')" | ||||||
|     runs-on: "ubuntu-latest" |     runs-on: "ubuntu-latest" | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ const config: Config.InitialOptions = { | |||||||
|   automock: false, |   automock: false, | ||||||
|   collectCoverage: true, |   collectCoverage: true, | ||||||
|   collectCoverageFrom: ["./src/**/**.ts"], |   collectCoverageFrom: ["./src/**/**.ts"], | ||||||
|   coverageProvider: "v8", |   coverageProvider: "babel", | ||||||
|   coverageThreshold: { |   coverageThreshold: { | ||||||
|     global: { |     global: { | ||||||
|       lines: 1 |       lines: 1 | ||||||
|  | |||||||
| @ -155,10 +155,6 @@ | |||||||
|     "API": "https://mempool.space/api/v1/services", |     "API": "https://mempool.space/api/v1/services", | ||||||
|     "ACCELERATIONS": false |     "ACCELERATIONS": false | ||||||
|   }, |   }, | ||||||
|   "STRATUM": { |  | ||||||
|     "ENABLED": false, |  | ||||||
|     "API": "http://localhost:1234" |  | ||||||
|   }, |  | ||||||
|   "FIAT_PRICE": { |   "FIAT_PRICE": { | ||||||
|     "ENABLED": true, |     "ENABLED": true, | ||||||
|     "PAID": false, |     "PAID": false, | ||||||
|  | |||||||
							
								
								
									
										55
									
								
								backend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										55
									
								
								backend/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -10,6 +10,7 @@ | |||||||
|       "hasInstallScript": true, |       "hasInstallScript": true, | ||||||
|       "license": "GNU Affero General Public License v3.0", |       "license": "GNU Affero General Public License v3.0", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|  |         "@babel/core": "^7.25.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.7.2", |         "axios": "1.7.2", | ||||||
| @ -17,7 +18,7 @@ | |||||||
|         "crypto-js": "~4.2.0", |         "crypto-js": "~4.2.0", | ||||||
|         "express": "~4.21.1", |         "express": "~4.21.1", | ||||||
|         "maxmind": "~4.3.11", |         "maxmind": "~4.3.11", | ||||||
|         "mysql2": "~3.12.0", |         "mysql2": "~3.11.0", | ||||||
|         "redis": "^4.7.0", |         "redis": "^4.7.0", | ||||||
|         "rust-gbt": "file:./rust-gbt", |         "rust-gbt": "file:./rust-gbt", | ||||||
|         "socks-proxy-agent": "~7.0.0", |         "socks-proxy-agent": "~7.0.0", | ||||||
| @ -25,6 +26,8 @@ | |||||||
|         "ws": "~8.18.0" |         "ws": "~8.18.0" | ||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
|  |         "@babel/code-frame": "^7.18.6", | ||||||
|  |         "@babel/core": "^7.25.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", | ||||||
| @ -5997,21 +6000,6 @@ | |||||||
|         "yallist": "^3.0.2" |         "yallist": "^3.0.2" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/lru.min": { |  | ||||||
|       "version": "1.1.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.1.tgz", |  | ||||||
|       "integrity": "sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==", |  | ||||||
|       "license": "MIT", |  | ||||||
|       "engines": { |  | ||||||
|         "bun": ">=1.0.0", |  | ||||||
|         "deno": ">=1.30.0", |  | ||||||
|         "node": ">=8.0.0" |  | ||||||
|       }, |  | ||||||
|       "funding": { |  | ||||||
|         "type": "github", |  | ||||||
|         "url": "https://github.com/sponsors/wellwelwel" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/make-dir": { |     "node_modules/make-dir": { | ||||||
|       "version": "3.1.0", |       "version": "3.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", |       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", | ||||||
| @ -6173,17 +6161,16 @@ | |||||||
|       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" |       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" | ||||||
|     }, |     }, | ||||||
|     "node_modules/mysql2": { |     "node_modules/mysql2": { | ||||||
|       "version": "3.12.0", |       "version": "3.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz", |       "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz", | ||||||
|       "integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==", |       "integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==", | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "aws-ssl-profiles": "^1.1.1", |         "aws-ssl-profiles": "^1.1.1", | ||||||
|         "denque": "^2.1.0", |         "denque": "^2.1.0", | ||||||
|         "generate-function": "^2.3.1", |         "generate-function": "^2.3.1", | ||||||
|         "iconv-lite": "^0.6.3", |         "iconv-lite": "^0.6.3", | ||||||
|         "long": "^5.2.1", |         "long": "^5.2.1", | ||||||
|         "lru.min": "^1.0.0", |         "lru-cache": "^8.0.0", | ||||||
|         "named-placeholders": "^1.1.3", |         "named-placeholders": "^1.1.3", | ||||||
|         "seq-queue": "^0.0.5", |         "seq-queue": "^0.0.5", | ||||||
|         "sqlstring": "^2.3.2" |         "sqlstring": "^2.3.2" | ||||||
| @ -6203,6 +6190,14 @@ | |||||||
|         "node": ">=0.10.0" |         "node": ">=0.10.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/mysql2/node_modules/lru-cache": { | ||||||
|  |       "version": "8.0.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", | ||||||
|  |       "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=16.14" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/named-placeholders": { |     "node_modules/named-placeholders": { | ||||||
|       "version": "1.1.3", |       "version": "1.1.3", | ||||||
|       "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", |       "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", | ||||||
| @ -12218,11 +12213,6 @@ | |||||||
|         "yallist": "^3.0.2" |         "yallist": "^3.0.2" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "lru.min": { |  | ||||||
|       "version": "1.1.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.1.tgz", |  | ||||||
|       "integrity": "sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==" |  | ||||||
|     }, |  | ||||||
|     "make-dir": { |     "make-dir": { | ||||||
|       "version": "3.1.0", |       "version": "3.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", |       "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", | ||||||
| @ -12337,16 +12327,16 @@ | |||||||
|       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" |       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" | ||||||
|     }, |     }, | ||||||
|     "mysql2": { |     "mysql2": { | ||||||
|       "version": "3.12.0", |       "version": "3.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.12.0.tgz", |       "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz", | ||||||
|       "integrity": "sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==", |       "integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "aws-ssl-profiles": "^1.1.1", |         "aws-ssl-profiles": "^1.1.1", | ||||||
|         "denque": "^2.1.0", |         "denque": "^2.1.0", | ||||||
|         "generate-function": "^2.3.1", |         "generate-function": "^2.3.1", | ||||||
|         "iconv-lite": "^0.6.3", |         "iconv-lite": "^0.6.3", | ||||||
|         "long": "^5.2.1", |         "long": "^5.2.1", | ||||||
|         "lru.min": "^1.0.0", |         "lru-cache": "^8.0.0", | ||||||
|         "named-placeholders": "^1.1.3", |         "named-placeholders": "^1.1.3", | ||||||
|         "seq-queue": "^0.0.5", |         "seq-queue": "^0.0.5", | ||||||
|         "sqlstring": "^2.3.2" |         "sqlstring": "^2.3.2" | ||||||
| @ -12359,6 +12349,11 @@ | |||||||
|           "requires": { |           "requires": { | ||||||
|             "safer-buffer": ">= 2.1.2 < 3.0.0" |             "safer-buffer": ">= 2.1.2 < 3.0.0" | ||||||
|           } |           } | ||||||
|  |         }, | ||||||
|  |         "lru-cache": { | ||||||
|  |           "version": "8.0.5", | ||||||
|  |           "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", | ||||||
|  |           "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==" | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  | |||||||
| @ -39,6 +39,7 @@ | |||||||
|     "prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\"" |     "prettier": "./node_modules/.bin/prettier --write \"src/**/*.{js,ts}\"" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "@babel/core": "^7.25.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.7.2", |     "axios": "1.7.2", | ||||||
| @ -46,7 +47,7 @@ | |||||||
|     "crypto-js": "~4.2.0", |     "crypto-js": "~4.2.0", | ||||||
|     "express": "~4.21.1", |     "express": "~4.21.1", | ||||||
|     "maxmind": "~4.3.11", |     "maxmind": "~4.3.11", | ||||||
|     "mysql2": "~3.12.0", |     "mysql2": "~3.11.0", | ||||||
|     "rust-gbt": "file:./rust-gbt", |     "rust-gbt": "file:./rust-gbt", | ||||||
|     "redis": "^4.7.0", |     "redis": "^4.7.0", | ||||||
|     "socks-proxy-agent": "~7.0.0", |     "socks-proxy-agent": "~7.0.0", | ||||||
| @ -54,6 +55,8 @@ | |||||||
|     "ws": "~8.18.0" |     "ws": "~8.18.0" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |     "@babel/code-frame": "^7.18.6", | ||||||
|  |     "@babel/core": "^7.25.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", | ||||||
|  | |||||||
| @ -151,9 +151,5 @@ | |||||||
|     "ENABLED": true, |     "ENABLED": true, | ||||||
|     "PAID": false, |     "PAID": false, | ||||||
|     "API_KEY": "__MEMPOOL_CURRENCY_API_KEY__" |     "API_KEY": "__MEMPOOL_CURRENCY_API_KEY__" | ||||||
|   }, |  | ||||||
|   "STRATUM": { |  | ||||||
|     "ENABLED": false, |  | ||||||
|     "API": "http://localhost:1234" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -159,11 +159,6 @@ describe('Mempool Backend Config', () => { | |||||||
|         PAID: false, |         PAID: false, | ||||||
|         API_KEY: '', |         API_KEY: '', | ||||||
|       }); |       }); | ||||||
| 
 |  | ||||||
|       expect(config.STRATUM).toStrictEqual({ |  | ||||||
|         ENABLED: false, |  | ||||||
|         API: 'http://localhost:1234', |  | ||||||
|       }); |  | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -21,7 +21,6 @@ import transactionRepository from '../../repositories/TransactionRepository'; | |||||||
| import rbfCache from '../rbf-cache'; | import rbfCache from '../rbf-cache'; | ||||||
| import { calculateMempoolTxCpfp } from '../cpfp'; | import { calculateMempoolTxCpfp } from '../cpfp'; | ||||||
| import { handleError } from '../../utils/api'; | import { handleError } from '../../utils/api'; | ||||||
| import poolsUpdater from '../../tasks/pools-updater'; |  | ||||||
| 
 | 
 | ||||||
| const TXID_REGEX = /^[a-f0-9]{64}$/i; | const TXID_REGEX = /^[a-f0-9]{64}$/i; | ||||||
| const BLOCK_HASH_REGEX = /^[a-f0-9]{64}$/i; | const BLOCK_HASH_REGEX = /^[a-f0-9]{64}$/i; | ||||||
| @ -57,10 +56,6 @@ class BitcoinRoutes { | |||||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-bulk/:from/:to', this.getBlocksByBulk.bind(this)) |       .get(config.MEMPOOL.API_URL_PREFIX + 'blocks-bulk/:from/:to', this.getBlocksByBulk.bind(this)) | ||||||
|       // Temporarily add txs/package endpoint for all backends until esplora supports it
 |       // Temporarily add txs/package endpoint for all backends until esplora supports it
 | ||||||
|       .post(config.MEMPOOL.API_URL_PREFIX + 'txs/package', this.$submitPackage) |       .post(config.MEMPOOL.API_URL_PREFIX + 'txs/package', this.$submitPackage) | ||||||
|       // Internal routes
 |  | ||||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'internal/blocks/definition/list', this.getBlockDefinitionHashes) |  | ||||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'internal/blocks/definition/current', this.getCurrentBlockDefinitionHash) |  | ||||||
|       .get(config.MEMPOOL.API_URL_PREFIX + 'internal/blocks/:definitionHash', this.getBlocksByDefinitionHash) |  | ||||||
|       ; |       ; | ||||||
| 
 | 
 | ||||||
|       if (config.MEMPOOL.BACKEND !== 'esplora') { |       if (config.MEMPOOL.BACKEND !== 'esplora') { | ||||||
| @ -744,52 +739,6 @@ class BitcoinRoutes { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async getBlockDefinitionHashes(req: Request, res: Response): Promise<void> { |  | ||||||
|     try { |  | ||||||
|       const result = await blocks.$getBlockDefinitionHashes(); |  | ||||||
|       if (!result) { |  | ||||||
|         handleError(req, res, 503, `Service Temporarily Unavailable`); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       res.setHeader('content-type', 'application/json'); |  | ||||||
|       res.send(result); |  | ||||||
|     } catch (e) { |  | ||||||
|       handleError(req, res, 500, e instanceof Error ? e.message : e); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private async getCurrentBlockDefinitionHash(req: Request, res: Response): Promise<void> { |  | ||||||
|     try { |  | ||||||
|       const currentSha = await poolsUpdater.getShaFromDb(); |  | ||||||
|       if (!currentSha) { |  | ||||||
|         handleError(req, res, 503, `Service Temporarily Unavailable`); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       res.setHeader('content-type', 'text/plain'); |  | ||||||
|       res.send(currentSha); |  | ||||||
|     } catch (e) { |  | ||||||
|       handleError(req, res, 500, e instanceof Error ? e.message : e); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private async getBlocksByDefinitionHash(req: Request, res: Response): Promise<void> { |  | ||||||
|     try { |  | ||||||
|       if (typeof(req.params.definitionHash) !== 'string') { |  | ||||||
|         res.status(400).send('Parameter "hash" must be a valid string'); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       const blocksHash = await blocks.$getBlocksByDefinitionHash(req.params.definitionHash as string); |  | ||||||
|       if (!blocksHash) { |  | ||||||
|         handleError(req, res, 503, `Service Temporarily Unavailable`); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       res.setHeader('content-type', 'application/json'); |  | ||||||
|       res.send(blocksHash); |  | ||||||
|     } catch (e) { |  | ||||||
|       handleError(req, res, 500, e instanceof Error ? e.message : e); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private getBlockTipHeight(req: Request, res: Response) { |   private getBlockTipHeight(req: Request, res: Response) { | ||||||
|     try { |     try { | ||||||
|       const result = blocks.getCurrentBlockHeight(); |       const result = blocks.getCurrentBlockHeight(); | ||||||
|  | |||||||
| @ -33,8 +33,8 @@ import AccelerationRepository from '../repositories/AccelerationRepository'; | |||||||
| import { calculateFastBlockCpfp, calculateGoodBlockCpfp } from './cpfp'; | import { calculateFastBlockCpfp, calculateGoodBlockCpfp } from './cpfp'; | ||||||
| import mempool from './mempool'; | import mempool from './mempool'; | ||||||
| import CpfpRepository from '../repositories/CpfpRepository'; | import CpfpRepository from '../repositories/CpfpRepository'; | ||||||
|  | import accelerationApi from './services/acceleration'; | ||||||
| import { parseDATUMTemplateCreator } from '../utils/bitcoin-script'; | import { parseDATUMTemplateCreator } from '../utils/bitcoin-script'; | ||||||
| import database from '../database'; |  | ||||||
| 
 | 
 | ||||||
| class Blocks { | class Blocks { | ||||||
|   private blocks: BlockExtended[] = []; |   private blocks: BlockExtended[] = []; | ||||||
| @ -1462,36 +1462,6 @@ class Blocks { | |||||||
|       // not a fatal error, we'll try again next time the indexer runs
 |       // not a fatal error, we'll try again next time the indexer runs
 | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   public async $getBlockDefinitionHashes(): Promise<string[] | null> { |  | ||||||
|     try { |  | ||||||
|       const [rows]: any = await database.query(`SELECT DISTINCT(definition_hash) FROM blocks`); |  | ||||||
|       if (rows && Array.isArray(rows)) { |  | ||||||
|         return rows.map(r => r.definition_hash); |  | ||||||
|       } else { |  | ||||||
|         logger.debug(`Unable to retreive list of blocks.definition_hash from db (no result)`); |  | ||||||
|         return null; |  | ||||||
|       } |  | ||||||
|     } catch (e) { |  | ||||||
|       logger.debug(`Unable to retreive list of blocks.definition_hash from db (exception: ${e})`); |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public async $getBlocksByDefinitionHash(definitionHash: string): Promise<string[] | null> { |  | ||||||
|     try { |  | ||||||
|       const [rows]: any = await database.query(`SELECT hash FROM blocks WHERE definition_hash = ?`, [definitionHash]); |  | ||||||
|       if (rows && Array.isArray(rows)) { |  | ||||||
|         return rows.map(r => r.hash); |  | ||||||
|       } else { |  | ||||||
|         logger.debug(`Unable to retreive list of blocks for definition hash ${definitionHash} from db (no result)`); |  | ||||||
|         return null; |  | ||||||
|       } |  | ||||||
|     } catch (e) { |  | ||||||
|       logger.debug(`Unable to retreive list of blocks for definition hash ${definitionHash} from db (exception: ${e})`); |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default new Blocks(); | export default new Blocks(); | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; | |||||||
| import { RowDataPacket } from 'mysql2'; | import { RowDataPacket } from 'mysql2'; | ||||||
| 
 | 
 | ||||||
| class DatabaseMigration { | class DatabaseMigration { | ||||||
|   private static currentVersion = 95; |   private static currentVersion = 94; | ||||||
|   private queryTimeout = 3600_000; |   private queryTimeout = 3600_000; | ||||||
|   private statisticsAddedIndexed = false; |   private statisticsAddedIndexed = false; | ||||||
|   private uniqueLogs: string[] = []; |   private uniqueLogs: string[] = []; | ||||||
| @ -1118,18 +1118,6 @@ class DatabaseMigration { | |||||||
|       } |       } | ||||||
|       await this.updateToSchemaVersion(94); |       await this.updateToSchemaVersion(94); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     // blocks pools-v2.json hash
 |  | ||||||
|     if (databaseSchemaVersion < 95) { |  | ||||||
|       let poolJsonSha = 'f737d86571d190cf1a1a3cf5fd86b33ba9624254'; |  | ||||||
|       const [poolJsonShaDb]: any[] = await DB.query(`SELECT string FROM state WHERE name = 'pools_json_sha'`); |  | ||||||
|       if (poolJsonShaDb?.length > 0) { |  | ||||||
|         poolJsonSha = poolJsonShaDb[0].string; |  | ||||||
|       } |  | ||||||
|       await this.$executeQuery(`ALTER TABLE blocks ADD definition_hash varchar(255) NOT NULL DEFAULT "${poolJsonSha}"`); |  | ||||||
|       await this.$executeQuery('ALTER TABLE blocks ADD INDEX `definition_hash` (`definition_hash`)'); |  | ||||||
|       await this.updateToSchemaVersion(95); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|  | |||||||
| @ -19,6 +19,15 @@ class PoolsParser { | |||||||
|     'addresses': '[]', |     'addresses': '[]', | ||||||
|     'slug': 'unknown' |     'slug': 'unknown' | ||||||
|   }; |   }; | ||||||
|  |   private uniqueLogs: string[] = []; | ||||||
|  | 
 | ||||||
|  |   private uniqueLog(loggerFunction: any, msg: string): void { | ||||||
|  |     if (this.uniqueLogs.includes(msg)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     this.uniqueLogs.push(msg); | ||||||
|  |     loggerFunction(msg); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   public setMiningPools(pools): void { |   public setMiningPools(pools): void { | ||||||
|     for (const pool of pools) { |     for (const pool of pools) { | ||||||
|  | |||||||
| @ -119,11 +119,7 @@ class RbfCache { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   public add(replaced: MempoolTransactionExtended[], newTxExtended: MempoolTransactionExtended): void { |   public add(replaced: MempoolTransactionExtended[], newTxExtended: MempoolTransactionExtended): void { | ||||||
|     if ( !newTxExtended |     if (!newTxExtended || !replaced?.length || this.txs.has(newTxExtended.txid)) { | ||||||
|       || !replaced?.length |  | ||||||
|       || this.txs.has(newTxExtended.txid) |  | ||||||
|       || !(replaced.some(tx => !this.replacedBy.has(tx.txid))) |  | ||||||
|     ) { |  | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,105 +0,0 @@ | |||||||
| import { WebSocket } from 'ws'; |  | ||||||
| import logger from '../../logger'; |  | ||||||
| import config from '../../config'; |  | ||||||
| import websocketHandler from '../websocket-handler'; |  | ||||||
| 
 |  | ||||||
| export interface StratumJob { |  | ||||||
|   pool: number; |  | ||||||
|   height: number; |  | ||||||
|   coinbase: string; |  | ||||||
|   scriptsig: string; |  | ||||||
|   reward: number; |  | ||||||
|   jobId: string; |  | ||||||
|   extraNonce: string; |  | ||||||
|   extraNonce2Size: number; |  | ||||||
|   prevHash: string; |  | ||||||
|   coinbase1: string; |  | ||||||
|   coinbase2: string; |  | ||||||
|   merkleBranches: string[]; |  | ||||||
|   version: string; |  | ||||||
|   bits: string; |  | ||||||
|   time: string; |  | ||||||
|   timestamp: number; |  | ||||||
|   cleanJobs: boolean; |  | ||||||
|   received: number; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function isStratumJob(obj: any): obj is StratumJob { |  | ||||||
|   return obj |  | ||||||
|     && typeof obj === 'object' |  | ||||||
|     && 'pool' in obj |  | ||||||
|     && 'prevHash' in obj |  | ||||||
|     && 'height' in obj |  | ||||||
|     && 'received' in obj |  | ||||||
|     && 'version' in obj |  | ||||||
|     && 'timestamp' in obj |  | ||||||
|     && 'bits' in obj |  | ||||||
|     && 'merkleBranches' in obj |  | ||||||
|     && 'cleanJobs' in obj; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class StratumApi { |  | ||||||
|   private ws: WebSocket | null = null; |  | ||||||
|   private runWebsocketLoop: boolean = false; |  | ||||||
|   private startedWebsocketLoop: boolean = false; |  | ||||||
|   private websocketConnected: boolean = false; |  | ||||||
|   private jobs: Record<string, StratumJob> = {}; |  | ||||||
| 
 |  | ||||||
|   public constructor() {} |  | ||||||
| 
 |  | ||||||
|   public getJobs(): Record<string, StratumJob> { |  | ||||||
|     return this.jobs; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private handleWebsocketMessage(msg: any): void { |  | ||||||
|     if (isStratumJob(msg)) { |  | ||||||
|       this.jobs[msg.pool] = msg; |  | ||||||
|       websocketHandler.handleNewStratumJob(this.jobs[msg.pool]); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public async connectWebsocket(): Promise<void> { |  | ||||||
|     if (!config.STRATUM.ENABLED) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     this.runWebsocketLoop = true; |  | ||||||
|     if (this.startedWebsocketLoop) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     while (this.runWebsocketLoop) { |  | ||||||
|       this.startedWebsocketLoop = true; |  | ||||||
|       if (!this.ws) { |  | ||||||
|         this.ws = new WebSocket(`${config.STRATUM.API}`); |  | ||||||
|         this.websocketConnected = true; |  | ||||||
| 
 |  | ||||||
|         this.ws.on('open', () => { |  | ||||||
|           logger.info('Stratum websocket opened'); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         this.ws.on('error', (error) => { |  | ||||||
|           logger.err('Stratum websocket error: ' + error); |  | ||||||
|           this.ws = null; |  | ||||||
|           this.websocketConnected = false; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         this.ws.on('close', () => { |  | ||||||
|           logger.info('Stratum websocket closed'); |  | ||||||
|           this.ws = null; |  | ||||||
|           this.websocketConnected = false; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         this.ws.on('message', (data, isBinary) => { |  | ||||||
|           try { |  | ||||||
|             const parsedMsg = JSON.parse((isBinary ? data : data.toString()) as string); |  | ||||||
|             this.handleWebsocketMessage(parsedMsg); |  | ||||||
|           } catch (e) { |  | ||||||
|             logger.warn('Failed to parse stratum websocket message: ' + (e instanceof Error ? e.message : e)); |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|       await new Promise(resolve => setTimeout(resolve, 5000)); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default new StratumApi(); |  | ||||||
| @ -38,7 +38,6 @@ interface AddressTransactions { | |||||||
| import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; | import bitcoinSecondClient from './bitcoin/bitcoin-second-client'; | ||||||
| import { calculateMempoolTxCpfp } from './cpfp'; | import { calculateMempoolTxCpfp } from './cpfp'; | ||||||
| import { getRecentFirstSeen } from '../utils/file-read'; | import { getRecentFirstSeen } from '../utils/file-read'; | ||||||
| import stratumApi, { StratumJob } from './services/stratum'; |  | ||||||
| 
 | 
 | ||||||
| // valid 'want' subscriptions
 | // valid 'want' subscriptions
 | ||||||
| const wantable = [ | const wantable = [ | ||||||
| @ -404,16 +403,6 @@ class WebsocketHandler { | |||||||
|             delete client['track-mempool']; |             delete client['track-mempool']; | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           if (parsedMessage && parsedMessage['track-stratum'] != null) { |  | ||||||
|             if (parsedMessage['track-stratum']) { |  | ||||||
|               const sub = parsedMessage['track-stratum']; |  | ||||||
|               client['track-stratum'] = sub; |  | ||||||
|               response['stratumJobs'] = this.socketData['stratumJobs']; |  | ||||||
|             } else { |  | ||||||
|               client['track-stratum'] = false; |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           if (Object.keys(response).length) { |           if (Object.keys(response).length) { | ||||||
|             client.send(this.serializeResponse(response)); |             client.send(this.serializeResponse(response)); | ||||||
|           } |           } | ||||||
| @ -1395,23 +1384,6 @@ class WebsocketHandler { | |||||||
|     await statistics.runStatistics(); |     await statistics.runStatistics(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public handleNewStratumJob(job: StratumJob): void { |  | ||||||
|     this.updateSocketDataFields({ 'stratumJobs': stratumApi.getJobs() }); |  | ||||||
| 
 |  | ||||||
|     for (const server of this.webSocketServers) { |  | ||||||
|       server.clients.forEach((client) => { |  | ||||||
|         if (client.readyState !== WebSocket.OPEN) { |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
|         if (client['track-stratum'] && (client['track-stratum'] === 'all' || client['track-stratum'] === job.pool)) { |  | ||||||
|           client.send(JSON.stringify({ |  | ||||||
|             'stratumJob': job |  | ||||||
|         })); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // takes a dictionary of JSON serialized values
 |   // takes a dictionary of JSON serialized values
 | ||||||
|   // and zips it together into a valid JSON object
 |   // and zips it together into a valid JSON object
 | ||||||
|   private serializeResponse(response): string { |   private serializeResponse(response): string { | ||||||
|  | |||||||
| @ -165,10 +165,6 @@ interface IConfig { | |||||||
|   WALLETS: { |   WALLETS: { | ||||||
|     ENABLED: boolean; |     ENABLED: boolean; | ||||||
|     WALLETS: string[]; |     WALLETS: string[]; | ||||||
|   }, |  | ||||||
|   STRATUM: { |  | ||||||
|     ENABLED: boolean; |  | ||||||
|     API: string; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -336,10 +332,6 @@ const defaults: IConfig = { | |||||||
|     'ENABLED': false, |     'ENABLED': false, | ||||||
|     'WALLETS': [], |     'WALLETS': [], | ||||||
|   }, |   }, | ||||||
|   'STRATUM': { |  | ||||||
|     'ENABLED': false, |  | ||||||
|     'API': 'http://localhost:1234', |  | ||||||
|   } |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class Config implements IConfig { | class Config implements IConfig { | ||||||
| @ -362,7 +354,6 @@ class Config implements IConfig { | |||||||
|   REDIS: IConfig['REDIS']; |   REDIS: IConfig['REDIS']; | ||||||
|   FIAT_PRICE: IConfig['FIAT_PRICE']; |   FIAT_PRICE: IConfig['FIAT_PRICE']; | ||||||
|   WALLETS: IConfig['WALLETS']; |   WALLETS: IConfig['WALLETS']; | ||||||
|   STRATUM: IConfig['STRATUM']; |  | ||||||
| 
 | 
 | ||||||
|   constructor() { |   constructor() { | ||||||
|     const configs = this.merge(configFromFile, defaults); |     const configs = this.merge(configFromFile, defaults); | ||||||
| @ -385,7 +376,6 @@ class Config implements IConfig { | |||||||
|     this.REDIS = configs.REDIS; |     this.REDIS = configs.REDIS; | ||||||
|     this.FIAT_PRICE = configs.FIAT_PRICE; |     this.FIAT_PRICE = configs.FIAT_PRICE; | ||||||
|     this.WALLETS = configs.WALLETS; |     this.WALLETS = configs.WALLETS; | ||||||
|     this.STRATUM = configs.STRATUM; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   merge = (...objects: object[]): IConfig => { |   merge = (...objects: object[]): IConfig => { | ||||||
|  | |||||||
| @ -48,7 +48,6 @@ import accelerationRoutes from './api/acceleration/acceleration.routes'; | |||||||
| import aboutRoutes from './api/about.routes'; | import aboutRoutes from './api/about.routes'; | ||||||
| import mempoolBlocks from './api/mempool-blocks'; | import mempoolBlocks from './api/mempool-blocks'; | ||||||
| import walletApi from './api/services/wallets'; | import walletApi from './api/services/wallets'; | ||||||
| import stratumApi from './api/services/stratum'; |  | ||||||
| 
 | 
 | ||||||
| class Server { | class Server { | ||||||
|   private wss: WebSocket.Server | undefined; |   private wss: WebSocket.Server | undefined; | ||||||
| @ -321,9 +320,6 @@ class Server { | |||||||
|     loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler)); |     loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler)); | ||||||
| 
 | 
 | ||||||
|     accelerationApi.connectWebsocket(); |     accelerationApi.connectWebsocket(); | ||||||
|     if (config.STRATUM.ENABLED) { |  | ||||||
|       stratumApi.connectWebsocket(); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setUpHttpApiRoutes(): void { |   setUpHttpApiRoutes(): void { | ||||||
|  | |||||||
| @ -325,8 +325,6 @@ export interface BlockExtension { | |||||||
|   // Requires coinstatsindex, will be set to NULL otherwise
 |   // Requires coinstatsindex, will be set to NULL otherwise
 | ||||||
|   utxoSetSize: number | null; |   utxoSetSize: number | null; | ||||||
|   totalInputAmt: number | null; |   totalInputAmt: number | null; | ||||||
|   // pools-v2.json git hash
 |  | ||||||
|   definitionHash: string | undefined; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
| @ -15,7 +15,6 @@ import blocks from '../api/blocks'; | |||||||
| import BlocksAuditsRepository from './BlocksAuditsRepository'; | import BlocksAuditsRepository from './BlocksAuditsRepository'; | ||||||
| import transactionUtils from '../api/transaction-utils'; | import transactionUtils from '../api/transaction-utils'; | ||||||
| import { parseDATUMTemplateCreator } from '../utils/bitcoin-script'; | import { parseDATUMTemplateCreator } from '../utils/bitcoin-script'; | ||||||
| import poolsUpdater from '../tasks/pools-updater'; |  | ||||||
| 
 | 
 | ||||||
| interface DatabaseBlock { | interface DatabaseBlock { | ||||||
|   id: string; |   id: string; | ||||||
| @ -124,7 +123,7 @@ class BlocksRepository { | |||||||
|         coinbase_signature, utxoset_size,        utxoset_change,    avg_tx_size, |         coinbase_signature, utxoset_size,        utxoset_change,    avg_tx_size, | ||||||
|         total_inputs,       total_outputs,       total_input_amt,   total_output_amt, |         total_inputs,       total_outputs,       total_input_amt,   total_output_amt, | ||||||
|         fee_percentiles,    segwit_total_txs,    segwit_total_size, segwit_total_weight, |         fee_percentiles,    segwit_total_txs,    segwit_total_size, segwit_total_weight, | ||||||
|         median_fee_amt,     coinbase_signature_ascii, definition_hash |         median_fee_amt,     coinbase_signature_ascii | ||||||
|       ) VALUE ( |       ) VALUE ( | ||||||
|         ?, ?, FROM_UNIXTIME(?), ?, |         ?, ?, FROM_UNIXTIME(?), ?, | ||||||
|         ?, ?, ?, ?, |         ?, ?, ?, ?, | ||||||
| @ -135,7 +134,7 @@ class BlocksRepository { | |||||||
|         ?, ?, ?, ?, |         ?, ?, ?, ?, | ||||||
|         ?, ?, ?, ?, |         ?, ?, ?, ?, | ||||||
|         ?, ?, ?, ?, |         ?, ?, ?, ?, | ||||||
|         ?, ?, ? |         ?, ? | ||||||
|       )`;
 |       )`;
 | ||||||
| 
 | 
 | ||||||
|       const poolDbId = await PoolsRepository.$getPoolByUniqueId(block.extras.pool.id); |       const poolDbId = await PoolsRepository.$getPoolByUniqueId(block.extras.pool.id); | ||||||
| @ -182,7 +181,6 @@ class BlocksRepository { | |||||||
|         block.extras.segwitTotalWeight, |         block.extras.segwitTotalWeight, | ||||||
|         block.extras.medianFeeAmt, |         block.extras.medianFeeAmt, | ||||||
|         truncatedCoinbaseSignatureAscii, |         truncatedCoinbaseSignatureAscii, | ||||||
|         poolsUpdater.currentSha |  | ||||||
|       ]; |       ]; | ||||||
| 
 | 
 | ||||||
|       await DB.query(query, params); |       await DB.query(query, params); | ||||||
| @ -1015,9 +1013,9 @@ class BlocksRepository { | |||||||
|   public async $savePool(id: string, poolId: number): Promise<void> { |   public async $savePool(id: string, poolId: number): Promise<void> { | ||||||
|     try { |     try { | ||||||
|       await DB.query(` |       await DB.query(` | ||||||
|         UPDATE blocks SET pool_id = ?, definition_hash = ? |         UPDATE blocks SET pool_id = ? | ||||||
|         WHERE hash = ?`,
 |         WHERE hash = ?`,
 | ||||||
|         [poolId, poolsUpdater.currentSha, id] |         [poolId, id] | ||||||
|       ); |       ); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       logger.err(`Cannot update block pool. Reason: ` + (e instanceof Error ? e.message : e)); |       logger.err(`Cannot update block pool. Reason: ` + (e instanceof Error ? e.message : e)); | ||||||
|  | |||||||
| @ -88,8 +88,8 @@ class PoolsUpdater { | |||||||
| 
 | 
 | ||||||
|       try { |       try { | ||||||
|         await DB.query('START TRANSACTION;'); |         await DB.query('START TRANSACTION;'); | ||||||
|         await this.updateDBSha(githubSha); |  | ||||||
|         await poolsParser.migratePoolsJson(); |         await poolsParser.migratePoolsJson(); | ||||||
|  |         await this.updateDBSha(githubSha); | ||||||
|         await DB.query('COMMIT;'); |         await DB.query('COMMIT;'); | ||||||
|       } catch (e) { |       } catch (e) { | ||||||
|         logger.err(`Could not migrate mining pools, rolling back. Exception: ${JSON.stringify(e)}`, this.tag); |         logger.err(`Could not migrate mining pools, rolling back. Exception: ${JSON.stringify(e)}`, this.tag); | ||||||
| @ -121,7 +121,7 @@ class PoolsUpdater { | |||||||
|   /** |   /** | ||||||
|    * Fetch our latest pools-v2.json sha from the db |    * Fetch our latest pools-v2.json sha from the db | ||||||
|    */ |    */ | ||||||
|   public async getShaFromDb(): Promise<string | null> { |   private async getShaFromDb(): Promise<string | null> { | ||||||
|     try { |     try { | ||||||
|       const [rows]: any[] = await DB.query('SELECT string FROM state WHERE name="pools_json_sha"'); |       const [rows]: any[] = await DB.query('SELECT string FROM state WHERE name="pools_json_sha"'); | ||||||
|       return (rows.length > 0 ? rows[0].string : null); |       return (rows.length > 0 ? rows[0].string : null); | ||||||
|  | |||||||
| @ -148,10 +148,6 @@ | |||||||
|     "API": "__MEMPOOL_SERVICES_API__", |     "API": "__MEMPOOL_SERVICES_API__", | ||||||
|     "ACCELERATIONS": __MEMPOOL_SERVICES_ACCELERATIONS__ |     "ACCELERATIONS": __MEMPOOL_SERVICES_ACCELERATIONS__ | ||||||
|   }, |   }, | ||||||
|   "STRATUM": { |  | ||||||
|     "ENABLED": __STRATUM_ENABLED__, |  | ||||||
|     "API": "__STRATUM_API__" |  | ||||||
|   }, |  | ||||||
|   "REDIS": { |   "REDIS": { | ||||||
|     "ENABLED": __REDIS_ENABLED__, |     "ENABLED": __REDIS_ENABLED__, | ||||||
|     "UNIX_SOCKET_PATH": "__REDIS_UNIX_SOCKET_PATH__", |     "UNIX_SOCKET_PATH": "__REDIS_UNIX_SOCKET_PATH__", | ||||||
|  | |||||||
| @ -149,10 +149,6 @@ __REPLICATION_SERVERS__=${REPLICATION_SERVERS:=[]} | |||||||
| __MEMPOOL_SERVICES_API__=${MEMPOOL_SERVICES_API:="https://mempool.space/api/v1/services"} | __MEMPOOL_SERVICES_API__=${MEMPOOL_SERVICES_API:="https://mempool.space/api/v1/services"} | ||||||
| __MEMPOOL_SERVICES_ACCELERATIONS__=${MEMPOOL_SERVICES_ACCELERATIONS:=false} | __MEMPOOL_SERVICES_ACCELERATIONS__=${MEMPOOL_SERVICES_ACCELERATIONS:=false} | ||||||
| 
 | 
 | ||||||
| # STRATUM |  | ||||||
| __STRATUM_ENABLED__=${STRATUM_ENABLED:=false} |  | ||||||
| __STRATUM_API__=${STRATUM_API:="http://localhost:1234"} |  | ||||||
| 
 |  | ||||||
| # REDIS | # REDIS | ||||||
| __REDIS_ENABLED__=${REDIS_ENABLED:=false} | __REDIS_ENABLED__=${REDIS_ENABLED:=false} | ||||||
| __REDIS_UNIX_SOCKET_PATH__=${REDIS_UNIX_SOCKET_PATH:=""} | __REDIS_UNIX_SOCKET_PATH__=${REDIS_UNIX_SOCKET_PATH:=""} | ||||||
| @ -304,10 +300,6 @@ sed -i "s!__REPLICATION_SERVERS__!${__REPLICATION_SERVERS__}!g" mempool-config.j | |||||||
| sed -i "s!__MEMPOOL_SERVICES_API__!${__MEMPOOL_SERVICES_API__}!g" mempool-config.json | sed -i "s!__MEMPOOL_SERVICES_API__!${__MEMPOOL_SERVICES_API__}!g" mempool-config.json | ||||||
| sed -i "s!__MEMPOOL_SERVICES_ACCELERATIONS__!${__MEMPOOL_SERVICES_ACCELERATIONS__}!g" mempool-config.json | sed -i "s!__MEMPOOL_SERVICES_ACCELERATIONS__!${__MEMPOOL_SERVICES_ACCELERATIONS__}!g" mempool-config.json | ||||||
| 
 | 
 | ||||||
| # STRATUM |  | ||||||
| sed -i "s!__STRATUM_ENABLED__!${__STRATUM_ENABLED__}!g" mempool-config.json |  | ||||||
| sed -i "s!__STRATUM_API__!${__STRATUM_API__}!g" mempool-config.json |  | ||||||
| 
 |  | ||||||
| # REDIS | # REDIS | ||||||
| sed -i "s!__REDIS_ENABLED__!${__REDIS_ENABLED__}!g" mempool-config.json | sed -i "s!__REDIS_ENABLED__!${__REDIS_ENABLED__}!g" mempool-config.json | ||||||
| sed -i "s!__REDIS_UNIX_SOCKET_PATH__!${__REDIS_UNIX_SOCKET_PATH__}!g" mempool-config.json | sed -i "s!__REDIS_UNIX_SOCKET_PATH__!${__REDIS_UNIX_SOCKET_PATH__}!g" mempool-config.json | ||||||
|  | |||||||
| @ -11,14 +11,10 @@ services: | |||||||
|     stop_grace_period: 1m |     stop_grace_period: 1m | ||||||
|     command: "./wait-for db:3306 --timeout=720 -- nginx -g 'daemon off;'" |     command: "./wait-for db:3306 --timeout=720 -- nginx -g 'daemon off;'" | ||||||
|     ports: |     ports: | ||||||
|       - 8080:8080 |       - 80:8080 | ||||||
|   api: |   api: | ||||||
|     environment: |     environment: | ||||||
|       MEMPOOL_BACKEND: "electrum" |       MEMPOOL_BACKEND: "none" | ||||||
|       ELECTRUM_HOST: "172.27.0.1" |  | ||||||
|       ELECTRUM_PORT: "50001" |  | ||||||
|       ELECTRUM_TLS_ENABLED: "false" |  | ||||||
| 
 |  | ||||||
|       CORE_RPC_HOST: "172.27.0.1" |       CORE_RPC_HOST: "172.27.0.1" | ||||||
|       CORE_RPC_PORT: "8332" |       CORE_RPC_PORT: "8332" | ||||||
|       CORE_RPC_USERNAME: "mempool" |       CORE_RPC_USERNAME: "mempool" | ||||||
|  | |||||||
| @ -45,7 +45,6 @@ __SERVICES_API__=${SERVICES_API:=https://mempool.space/api/v1/services} | |||||||
| __PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false} | __PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false} | ||||||
| __HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true} | __HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true} | ||||||
| __ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false} | __ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false} | ||||||
| __STRATUM_ENABLED__=${STRATUM_ENABLED:=false} |  | ||||||
| 
 | 
 | ||||||
| # Export as environment variables to be used by envsubst | # Export as environment variables to be used by envsubst | ||||||
| export __MAINNET_ENABLED__ | export __MAINNET_ENABLED__ | ||||||
| @ -77,7 +76,6 @@ export __SERVICES_API__ | |||||||
| export __PUBLIC_ACCELERATIONS__ | export __PUBLIC_ACCELERATIONS__ | ||||||
| export __HISTORICAL_PRICE__ | export __HISTORICAL_PRICE__ | ||||||
| export __ADDITIONAL_CURRENCIES__ | export __ADDITIONAL_CURRENCIES__ | ||||||
| export __STRATUM_ENABLED__ |  | ||||||
| 
 | 
 | ||||||
| folder=$(find /var/www/mempool -name "config.js" | xargs dirname) | folder=$(find /var/www/mempool -name "config.js" | xargs dirname) | ||||||
| echo ${folder} | echo ${folder} | ||||||
|  | |||||||
| @ -13,8 +13,8 @@ localhostIP="127.0.0.1" | |||||||
| cp ./docker/frontend/* ./frontend | cp ./docker/frontend/* ./frontend | ||||||
| cp ./nginx.conf ./frontend/ | cp ./nginx.conf ./frontend/ | ||||||
| cp ./nginx-mempool.conf ./frontend/ | cp ./nginx-mempool.conf ./frontend/ | ||||||
| # sed -i"" -e "s/${localhostIP}:80/0.0.0.0:__MEMPOOL_FRONTEND_HTTP_PORT__/g" ./frontend/nginx.conf | sed -i"" -e "s/${localhostIP}:80/0.0.0.0:__MEMPOOL_FRONTEND_HTTP_PORT__/g" ./frontend/nginx.conf | ||||||
| # sed -i"" -e "s/${localhostIP}/0.0.0.0/g" ./frontend/nginx.conf | sed -i"" -e "s/${localhostIP}/0.0.0.0/g" ./frontend/nginx.conf | ||||||
| sed -i"" -e "s/user nobody;//g" ./frontend/nginx.conf | sed -i"" -e "s/user nobody;//g" ./frontend/nginx.conf | ||||||
| sed -i"" -e "s!/etc/nginx/nginx-mempool.conf!/etc/nginx/conf.d/nginx-mempool.conf!g" ./frontend/nginx.conf | sed -i"" -e "s!/etc/nginx/nginx-mempool.conf!/etc/nginx/conf.d/nginx-mempool.conf!g" ./frontend/nginx.conf | ||||||
| sed -i"" -e "s/${localhostIP}:8999/__MEMPOOL_BACKEND_MAINNET_HTTP_HOST__:__MEMPOOL_BACKEND_MAINNET_HTTP_PORT__/g" ./frontend/nginx-mempool.conf | sed -i"" -e "s/${localhostIP}:8999/__MEMPOOL_BACKEND_MAINNET_HTTP_HOST__:__MEMPOOL_BACKEND_MAINNET_HTTP_PORT__/g" ./frontend/nginx-mempool.conf | ||||||
|  | |||||||
| @ -344,9 +344,7 @@ describe('Mainnet', () => { | |||||||
|       cy.visit('/'); |       cy.visit('/'); | ||||||
|       cy.waitForSkeletonGone(); |       cy.waitForSkeletonGone(); | ||||||
| 
 | 
 | ||||||
|       //TODO(knorrium): add a check for the proxied server
 |       cy.changeNetwork('testnet4'); | ||||||
|       // cy.changeNetwork('testnet4');
 |  | ||||||
| 
 |  | ||||||
|       cy.changeNetwork('signet'); |       cy.changeNetwork('signet'); | ||||||
|       cy.changeNetwork('mainnet'); |       cy.changeNetwork('mainnet'); | ||||||
|     }); |     }); | ||||||
|  | |||||||
| @ -27,6 +27,5 @@ | |||||||
|   "ACCELERATOR": false, |   "ACCELERATOR": false, | ||||||
|   "ACCELERATOR_BUTTON": true, |   "ACCELERATOR_BUTTON": true, | ||||||
|   "PUBLIC_ACCELERATIONS": false, |   "PUBLIC_ACCELERATIONS": false, | ||||||
|   "STRATUM_ENABLED": false, |  | ||||||
|   "SERVICES_API": "https://mempool.space/api/v1/services" |   "SERVICES_API": "https://mempool.space/api/v1/services" | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										30
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -33,7 +33,7 @@ | |||||||
|         "browserify": "^17.0.0", |         "browserify": "^17.0.0", | ||||||
|         "clipboard": "^2.0.11", |         "clipboard": "^2.0.11", | ||||||
|         "domino": "^2.1.6", |         "domino": "^2.1.6", | ||||||
|         "echarts": "~5.6.0", |         "echarts": "~5.5.0", | ||||||
|         "esbuild": "^0.24.0", |         "esbuild": "^0.24.0", | ||||||
|         "ngx-echarts": "~17.2.0", |         "ngx-echarts": "~17.2.0", | ||||||
|         "ngx-infinite-scroll": "^17.0.0", |         "ngx-infinite-scroll": "^17.0.0", | ||||||
| @ -8724,12 +8724,12 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/echarts": { |     "node_modules/echarts": { | ||||||
|       "version": "5.6.0", |       "version": "5.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz", |       "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", | ||||||
|       "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", |       "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "tslib": "2.3.0", |         "tslib": "2.3.0", | ||||||
|         "zrender": "5.6.1" |         "zrender": "5.5.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/echarts/node_modules/tslib": { |     "node_modules/echarts/node_modules/tslib": { | ||||||
| @ -18366,9 +18366,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/zrender": { |     "node_modules/zrender": { | ||||||
|       "version": "5.6.1", |       "version": "5.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz", |       "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", | ||||||
|       "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", |       "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "tslib": "2.3.0" |         "tslib": "2.3.0" | ||||||
|       } |       } | ||||||
| @ -24485,12 +24485,12 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "echarts": { |     "echarts": { | ||||||
|       "version": "5.6.0", |       "version": "5.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz", |       "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz", | ||||||
|       "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", |       "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "tslib": "2.3.0", |         "tslib": "2.3.0", | ||||||
|         "zrender": "5.6.1" |         "zrender": "5.5.0" | ||||||
|       }, |       }, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "tslib": { |         "tslib": { | ||||||
| @ -31485,9 +31485,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "zrender": { |     "zrender": { | ||||||
|       "version": "5.6.1", |       "version": "5.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz", |       "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz", | ||||||
|       "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", |       "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "tslib": "2.3.0" |         "tslib": "2.3.0" | ||||||
|       }, |       }, | ||||||
|  | |||||||
| @ -86,7 +86,7 @@ | |||||||
|     "browserify": "^17.0.0", |     "browserify": "^17.0.0", | ||||||
|     "clipboard": "^2.0.11", |     "clipboard": "^2.0.11", | ||||||
|     "domino": "^2.1.6", |     "domino": "^2.1.6", | ||||||
|     "echarts": "~5.6.0", |     "echarts": "~5.5.0", | ||||||
|     "ngx-echarts": "~17.2.0", |     "ngx-echarts": "~17.2.0", | ||||||
|     "ngx-infinite-scroll": "^17.0.0", |     "ngx-infinite-scroll": "^17.0.0", | ||||||
|     "qrcode": "1.5.1", |     "qrcode": "1.5.1", | ||||||
|  | |||||||
| @ -3,10 +3,8 @@ const fs = require('fs'); | |||||||
| let PROXY_CONFIG = require('./proxy.conf'); | let PROXY_CONFIG = require('./proxy.conf'); | ||||||
| 
 | 
 | ||||||
| PROXY_CONFIG.forEach(entry => { | PROXY_CONFIG.forEach(entry => { | ||||||
|   const hostname = process.env.CYPRESS_REROUTE_TESTNET === 'true' ? 'mempool-staging.fra.mempool.space' : 'node201.fmt.mempool.space'; |   entry.target = entry.target.replace("mempool.space", "mempool-staging.fra.mempool.space"); | ||||||
|   console.log(`e2e tests running against ${hostname}`); |   entry.target = entry.target.replace("liquid.network", "liquid-staging.fra.mempool.space"); | ||||||
|   entry.target = entry.target.replace("mempool.space", hostname); |  | ||||||
|   entry.target = entry.target.replace("liquid.network", "liquid-staging.fmt.mempool.space"); |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| module.exports = PROXY_CONFIG; | module.exports = PROXY_CONFIG; | ||||||
|  | |||||||
| @ -217,7 +217,7 @@ | |||||||
|           <ng-container> |           <ng-container> | ||||||
|             <ng-template ngFor let-sponsor [ngForOf]="profiles.whales"> |             <ng-template ngFor let-sponsor [ngForOf]="profiles.whales"> | ||||||
|               <a [href]="'https://x.com/' + sponsor.username" target="_blank" rel="sponsored" [title]="sponsor.username"> |               <a [href]="'https://x.com/' + sponsor.username" target="_blank" rel="sponsored" [title]="sponsor.username"> | ||||||
|                 <img class="image" [src]="'/api/v1/services/account/images/' + sponsor.username" onError="this.src = '/resources/profile/grumpy.svg'; this.className = 'image unknown'"/> |                 <img class="image" [src]="'/api/v1/services/account/images/' + sponsor.username + '/md5=' + sponsor.imageMd5" onError="this.src = '/resources/profile/grumpy.svg'; this.className = 'image unknown'"/> | ||||||
|               </a> |               </a> | ||||||
|             </ng-template> |             </ng-template> | ||||||
|           </ng-container> |           </ng-container> | ||||||
| @ -229,7 +229,7 @@ | |||||||
|         <div class="wrapper"> |         <div class="wrapper"> | ||||||
|           <ng-template ngFor let-sponsor [ngForOf]="profiles.chads"> |           <ng-template ngFor let-sponsor [ngForOf]="profiles.chads"> | ||||||
|             <a [href]="'https://x.com/' + sponsor.username" target="_blank" rel="sponsored" [title]="sponsor.username"> |             <a [href]="'https://x.com/' + sponsor.username" target="_blank" rel="sponsored" [title]="sponsor.username"> | ||||||
|               <img class="image" [src]="'/api/v1/services/account/images/' + sponsor.username" onError="this.src = '/resources/profile/grumpy.svg'; this.className = 'image unknown'"/> |               <img class="image" [src]="'/api/v1/services/account/images/' + sponsor.username + '/md5=' + sponsor.imageMd5" onError="this.src = '/resources/profile/grumpy.svg'; this.className = 'image unknown'"/> | ||||||
|             </a> |             </a> | ||||||
|           </ng-template> |           </ng-template> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -1,18 +1,10 @@ | |||||||
| <div class="box card w-100 accelerate-checkout-inner" [class.input-disabled]="isCheckoutLocked > 0" style="background: var(--box-bg)" id=acceleratePreviewAnchor> | <div class="box card w-100" style="background: var(--box-bg)" id=acceleratePreviewAnchor> | ||||||
|   @if (accelerateError) { |   @if (accelerateError) { | ||||||
|     @if (accelerateError.includes('Payment declined')) { |  | ||||||
|       <div class="row mb-1 text-center"> |  | ||||||
|         <div class="col-sm"> |  | ||||||
|           <h1 style="font-size: larger;">{{ accelerateError }}</h1> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     } @else { |  | ||||||
|     <div class="row mb-1 text-center"> |     <div class="row mb-1 text-center"> | ||||||
|       <div class="col-sm"> |       <div class="col-sm"> | ||||||
|         <h1 style="font-size: larger;" i18n="accelerator.sorry-error-title">Sorry, something went wrong!</h1> |         <h1 style="font-size: larger;" i18n="accelerator.sorry-error-title">Sorry, something went wrong!</h1> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     } |  | ||||||
|     <div class="row text-center mt-1"> |     <div class="row text-center mt-1"> | ||||||
|       <div class="col-sm"> |       <div class="col-sm"> | ||||||
|         <div class="d-flex flex-row justify-content-center align-items-center"> |         <div class="d-flex flex-row justify-content-center align-items-center"> | ||||||
| @ -365,11 +357,11 @@ | |||||||
|           <app-active-acceleration-box [miningStats]="miningStats" [pools]="estimate.pools" [chartOnly]="true" class="ml-2"></app-active-acceleration-box> |           <app-active-acceleration-box [miningStats]="miningStats" [pools]="estimate.pools" [chartOnly]="true" class="ml-2"></app-active-acceleration-box> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div class="payment-area" style="font-size: 14px;"> |       <div class="payment-area mt-2 p-2" style="font-size: 14px;"> | ||||||
|         <div class="row text-center justify-content-center mx-2"> |         <div class="row text-center justify-content-center mx-2"> | ||||||
|           <span i18n="accelerator.payment-to-mempool-space">Payment to mempool.space for acceleration of txid <a [routerLink]="'/tx/' + tx.txid" target="_blank">{{ tx.txid.substr(0, 10) }}..{{ tx.txid.substr(-10) }}</a></span> |           <p i18n="accelerator.payment-to-mempool-space">Payment to mempool.space for acceleration of txid <a [routerLink]="'/tx/' + tx.txid" target="_blank">{{ tx.txid.substr(0, 10) }}..{{ tx.txid.substr(-10) }}</a></p> | ||||||
|         </div> |         </div> | ||||||
|         @if (canPayWithBalance || !(canPayWithBitcoin || canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay)) { |         @if (canPayWithBalance || !(canPayWithBitcoin || canPayWithCashapp)) { | ||||||
|           <div class="row"> |           <div class="row"> | ||||||
|             <div class="col-sm text-center d-flex flex-column justify-content-center align-items-center"> |             <div class="col-sm text-center d-flex flex-column justify-content-center align-items-center"> | ||||||
|               <p><ng-container i18n="accelerator.your-account-will-be-debited">Your account will be debited no more than</ng-container> <small style="font-family: monospace;">{{ cost | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></p> |               <p><ng-container i18n="accelerator.your-account-will-be-debited">Your account will be debited no more than</ng-container> <small style="font-family: monospace;">{{ cost | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></p> | ||||||
| @ -386,12 +378,9 @@ | |||||||
|                   <p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <span><small style="font-family: monospace;">{{ ((invoice.btcDue * 100_000_000) || cost) | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></span></p> |                   <p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <span><small style="font-family: monospace;">{{ ((invoice.btcDue * 100_000_000) || cost) | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></span></p> | ||||||
|                   <app-bitcoin-invoice style="width: 100%;" [invoice]="invoice" [minimal]="true" (completed)="bitcoinPaymentCompleted()"></app-bitcoin-invoice> |                   <app-bitcoin-invoice style="width: 100%;" [invoice]="invoice" [minimal]="true" (completed)="bitcoinPaymentCompleted()"></app-bitcoin-invoice> | ||||||
|                 } @else if (btcpayInvoiceFailed) { |                 } @else if (btcpayInvoiceFailed) { | ||||||
|                   <div class="btcpay-invoice"> |                   <p i18n="accelerator.failed-to-load-invoice">Failed to load invoice</p> | ||||||
|                     <fa-icon style="font-size: 20px; color: var(--red)" [icon]="['fas', 'circle-xmark']"></fa-icon> |                   <div class="d-flex flex-column align-items-center justify-content-center" style="width: 100%; height: 292px;"> | ||||||
|                     <span i18n="accelerator.failed-to-load-invoice">Failed to load invoice</span> |                     <fa-icon style="font-size: 24px; color: var(--red)" [icon]="['fas', 'circle-xmark']"></fa-icon> | ||||||
|                     @if (!loadingBtcpayInvoice) { |  | ||||||
|                       <button class="btn btn-sm btn-secondary mt-0 mt-md-1" (click)="requestBTCPayInvoice()">Retry ↻</button> |  | ||||||
|                     } |  | ||||||
|                   </div> |                   </div> | ||||||
|                 } @else { |                 } @else { | ||||||
|                   <p i18n="accelerator.loading-invoice">Loading invoice...</p> |                   <p i18n="accelerator.loading-invoice">Loading invoice...</p> | ||||||
| @ -400,13 +389,13 @@ | |||||||
|                   </div> |                   </div> | ||||||
|                 } |                 } | ||||||
|               </div> |               </div> | ||||||
|               @if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay || canPayWithCardOnFile) { |               @if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) { | ||||||
|                 <div class="col-sm text-center flex-grow-0  d-flex flex-column justify-content-center align-items-center"> |                 <div class="col-sm text-center flex-grow-0  d-flex flex-column justify-content-center align-items-center"> | ||||||
|                   <p class="text-nowrap">——<span i18n="or"> OR </span>——</p> |                   <p class="text-nowrap">—<span i18n="or">OR</span>—</p> | ||||||
|                 </div> |                 </div> | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|             @if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay || canPayWithCardOnFile) { |             @if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) { | ||||||
|               <div class="col-sm text-center d-flex flex-column justify-content-center align-items-center"> |               <div class="col-sm text-center d-flex flex-column justify-content-center align-items-center"> | ||||||
|                 <p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <app-fiat [value]="cost"></app-fiat> with</p> |                 <p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <app-fiat [value]="cost"></app-fiat> with</p> | ||||||
|                 @if (canPayWithCashapp) { |                 @if (canPayWithCashapp) { | ||||||
| @ -424,17 +413,6 @@ | |||||||
|                     <img src="/resources/google-pay.png" height=37> |                     <img src="/resources/google-pay.png" height=37> | ||||||
|                   </div> |                   </div> | ||||||
|                 } |                 } | ||||||
|                 @if (canPayWithCardOnFile) { |  | ||||||
|                   @if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) { <span class="mt-1 mb-1"></span> } |  | ||||||
|                   <div class="paymentMethod mx-2 d-flex justify-content-center align-items-center" style="width: 200px; height: 55px" (click)="moveToStep('cardonfile')"> |  | ||||||
|                     @if (['VISA', 'MASTERCARD', 'JCB', 'DISCOVER', 'DISCOVER_DINERS', 'AMERICAN_EXPRESS'].includes(estimate?.availablePaymentMethods?.cardOnFile?.card?.brand)) { |  | ||||||
|                       <app-svg-images [name]="estimate?.availablePaymentMethods?.cardOnFile?.card?.brand" height="33" class="mr-2"></app-svg-images> |  | ||||||
|                     } @else { |  | ||||||
|                       <app-svg-images name="OTHER_BRAND" height="33" class="mr-2"></app-svg-images> |  | ||||||
|                     } |  | ||||||
|                     <span style="font-size: 22px; padding-bottom: 3px">{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}</span> |  | ||||||
|                   </div> |  | ||||||
|                 } |  | ||||||
|               </div> |               </div> | ||||||
|             } |             } | ||||||
|           </div> |           </div> | ||||||
| @ -457,7 +435,7 @@ | |||||||
|         <button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="moveToStep('summary')" i18n="go-back">Go back</button> |         <button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="moveToStep('summary')" i18n="go-back">Go back</button> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   } @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay' || step === 'cardonfile') { |   } @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay') { | ||||||
|     <!-- Show checkout page --> |     <!-- Show checkout page --> | ||||||
|     <div class="row mb-md-1 text-center" id="confirm-title"> |     <div class="row mb-md-1 text-center" id="confirm-title"> | ||||||
|       <div class="col-sm" id="confirm-payment-title"> |       <div class="col-sm" id="confirm-payment-title"> | ||||||
| @ -473,7 +451,7 @@ | |||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     @if (step === 'cashapp' && !loadingCashapp || step === 'applepay' && !loadingApplePay || step === 'googlepay' && !loadingGooglePay || step === 'cardonfile' && !loadingCardOnFile) { |     @if (step === 'cashapp' && !loadingCashapp || step === 'applepay' && !loadingApplePay || step === 'googlepay' && !loadingGooglePay) { | ||||||
|       <div class="row text-center mt-1"> |       <div class="row text-center mt-1"> | ||||||
|         <div class="col-sm"> |         <div class="col-sm"> | ||||||
|           <div class="form-group w-100"> |           <div class="form-group w-100"> | ||||||
| @ -498,24 +476,14 @@ | |||||||
|             <div id="cash-app-pay" class="d-inline-block" style="height: 50px" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> |             <div id="cash-app-pay" class="d-inline-block" style="height: 50px" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> | ||||||
|           } @else if (step === 'googlepay') { |           } @else if (step === 'googlepay') { | ||||||
|             <div id="google-pay-button" class="d-inline-block" style="height: 50px" [style]="loadingGooglePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> |             <div id="google-pay-button" class="d-inline-block" style="height: 50px" [style]="loadingGooglePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> | ||||||
|           } @else if (step === 'cardonfile') { |  | ||||||
|             <div class="paymentMethod mx-2 d-flex justify-content-center align-items-center ml-auto mr-auto" style="width: 200px; height: 55px" (click)="requestCardOnFilePayment()" [style]="loadingCardOnFile ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"> |  | ||||||
|               <fa-icon style="font-size: 24px; color: white" [icon]="['fas', 'credit-card']"></fa-icon> |  | ||||||
|               <span class="ml-2" style="font-size: 22px">{{ estimate?.availablePaymentMethods?.cardOnFile?.card?.brand }} {{ estimate?.availablePaymentMethods?.cardOnFile?.card?.last_4 }}</span> |  | ||||||
|             </div> |  | ||||||
|           } |           } | ||||||
|           @if (loadingCashapp || loadingApplePay || loadingGooglePay || loadingCardOnFile) { |           @if (loadingCashapp || loadingApplePay || loadingGooglePay) { | ||||||
|           <div display="d-flex flex-row justify-content-center"> |           <div display="d-flex flex-row justify-content-center"> | ||||||
|             <span i18n="accelerator.loading-payment-method">Loading payment method...</span> |             <span i18n="accelerator.loading-payment-method">Loading payment method...</span> | ||||||
|             <div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div> |             <div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div> | ||||||
|           </div> |           </div> | ||||||
|           } |           } | ||||||
|         </div> |         </div> | ||||||
|         @if (isTokenizing > 0) { |  | ||||||
|           <div class="d-flex flex-row justify-content-center"> |  | ||||||
|             <div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div> |  | ||||||
|           </div> |  | ||||||
|         } |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,13 +8,6 @@ | |||||||
|   color: var(--green) |   color: var(--green) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .accelerate-checkout-inner { |  | ||||||
|   &.input-disabled { |  | ||||||
|     pointer-events: none; |  | ||||||
|     opacity: 0.75; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .paymentMethod { | .paymentMethod { | ||||||
|   padding: 10px; |   padding: 10px; | ||||||
|   background-color: var(--secondary); |   background-color: var(--secondary); | ||||||
| @ -153,11 +146,6 @@ | |||||||
| 
 | 
 | ||||||
| .payment-area { | .payment-area { | ||||||
|   background: var(--bg); |   background: var(--bg); | ||||||
|   margin-top: 0.5rem; |  | ||||||
|   padding: 0.5rem; |  | ||||||
|   @media (max-width: 575px) { |  | ||||||
|     padding-bottom: 1.25rem; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .col.pie { | .col.pie { | ||||||
| @ -225,16 +213,3 @@ | |||||||
| .apple-pay-button-white-with-line { | .apple-pay-button-white-with-line { | ||||||
|     -apple-pay-button-style: white-outline; |     -apple-pay-button-style: white-outline; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| .btcpay-invoice { |  | ||||||
|   display: flex; |  | ||||||
|   height: 292px; |  | ||||||
|   flex-direction: column; |  | ||||||
|   justify-content: center; |  | ||||||
|   align-items: center; |  | ||||||
|   @media (max-width: 575px) { |  | ||||||
|     height: 75px; |  | ||||||
|     flex-direction: row; |  | ||||||
|     gap: 5px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -13,7 +13,7 @@ import { EnterpriseService } from '@app/services/enterprise.service'; | |||||||
| import { ApiService } from '@app/services/api.service'; | import { ApiService } from '@app/services/api.service'; | ||||||
| import { isDevMode } from '@angular/core'; | import { isDevMode } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay' | 'cardOnFile'; | export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay'; | ||||||
| 
 | 
 | ||||||
| export type AccelerationEstimate = { | export type AccelerationEstimate = { | ||||||
|   hasAccess: boolean; |   hasAccess: boolean; | ||||||
| @ -26,7 +26,7 @@ export type AccelerationEstimate = { | |||||||
|   mempoolBaseFee: number; |   mempoolBaseFee: number; | ||||||
|   vsizeFee: number; |   vsizeFee: number; | ||||||
|   pools: number[]; |   pools: number[]; | ||||||
|   availablePaymentMethods: Record<PaymentMethod, {min: number, max: number, card?: {card_id: string, last_4: string, brand: string, name: string, billing: any}}>; |   availablePaymentMethods: Record<PaymentMethod, {min: number, max: number}>; | ||||||
|   unavailable?: boolean; |   unavailable?: boolean; | ||||||
|   options: { // recommended bid options
 |   options: { // recommended bid options
 | ||||||
|     fee: number; // recommended userBid in sats
 |     fee: number; // recommended userBid in sats
 | ||||||
| @ -49,7 +49,7 @@ export const MIN_BID_RATIO = 1; | |||||||
| export const DEFAULT_BID_RATIO = 2; | export const DEFAULT_BID_RATIO = 2; | ||||||
| export const MAX_BID_RATIO = 4; | export const MAX_BID_RATIO = 4; | ||||||
| 
 | 
 | ||||||
| type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'applepay' | 'googlepay' | 'cardonfile' | 'processing' | 'paid' | 'success'; | type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'applepay' | 'googlepay' | 'processing' | 'paid' | 'success'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-accelerate-checkout', |   selector: 'app-accelerate-checkout', | ||||||
| @ -62,9 +62,9 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|   @Input() miningStats: MiningStats; |   @Input() miningStats: MiningStats; | ||||||
|   @Input() eta: ETA; |   @Input() eta: ETA; | ||||||
|   @Input() scrollEvent: boolean; |   @Input() scrollEvent: boolean; | ||||||
|  |   @Input() cashappEnabled: boolean = true; | ||||||
|   @Input() applePayEnabled: boolean = false; |   @Input() applePayEnabled: boolean = false; | ||||||
|   @Input() googlePayEnabled: boolean = true; |   @Input() googlePayEnabled: boolean = true; | ||||||
|   @Input() cardOnFileEnabled: boolean = true; |  | ||||||
|   @Input() advancedEnabled: boolean = false; |   @Input() advancedEnabled: boolean = false; | ||||||
|   @Input() forceMobile: boolean = false; |   @Input() forceMobile: boolean = false; | ||||||
|   @Input() showDetails: boolean = false; |   @Input() showDetails: boolean = false; | ||||||
| @ -76,8 +76,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|   calculating = true; |   calculating = true; | ||||||
|   processing = false; |   processing = false; | ||||||
|   isCheckoutLocked = 0; // reference counter, 0 = unlocked, >0 = locked
 |  | ||||||
|   isTokenizing = 0; // reference counter, 0 = false, >0 = true
 |  | ||||||
|   selectedOption: 'wait' | 'accel'; |   selectedOption: 'wait' | 'accel'; | ||||||
|   cantPayReason = ''; |   cantPayReason = ''; | ||||||
|   quoteError = ''; // error fetching estimate or initial data
 |   quoteError = ''; // error fetching estimate or initial data
 | ||||||
| @ -117,7 +115,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|   loadingCashapp = false; |   loadingCashapp = false; | ||||||
|   loadingApplePay = false; |   loadingApplePay = false; | ||||||
|   loadingGooglePay = false; |   loadingGooglePay = false; | ||||||
|   loadingCardOnFile = false; |  | ||||||
|   payments: any; |   payments: any; | ||||||
|   cashAppPay: any; |   cashAppPay: any; | ||||||
|   applePay: any; |   applePay: any; | ||||||
| @ -157,7 +154,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|         this.accelerateError = null; |         this.accelerateError = null; | ||||||
|         this.timePaid = 0; |         this.timePaid = 0; | ||||||
|         this.btcpayInvoiceFailed = false; |         this.btcpayInvoiceFailed = false; | ||||||
|         this.moveToStep('summary', true); |         this.moveToStep('summary'); | ||||||
|       } else { |       } else { | ||||||
|         this.auth = auth; |         this.auth = auth; | ||||||
|       } |       } | ||||||
| @ -166,11 +163,11 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|     const urlParams = new URLSearchParams(window.location.search); |     const urlParams = new URLSearchParams(window.location.search); | ||||||
|     if (urlParams.get('cash_request_id')) { // Redirected from cashapp
 |     if (urlParams.get('cash_request_id')) { // Redirected from cashapp
 | ||||||
|       this.moveToStep('processing', true); |       this.moveToStep('processing'); | ||||||
|       this.insertSquare(); |       this.insertSquare(); | ||||||
|       this.setupSquare(); |       this.setupSquare(); | ||||||
|     } else { |     } else { | ||||||
|       this.moveToStep('summary', true); |       this.moveToStep('summary'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.conversionsSubscription = this.stateService.conversions$.subscribe( |     this.conversionsSubscription = this.stateService.conversions$.subscribe( | ||||||
| @ -195,23 +192,20 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|     } |     } | ||||||
|     if (changes.accelerating && this.accelerating) { |     if (changes.accelerating && this.accelerating) { | ||||||
|       if (this.step === 'processing' || this.step === 'paid') { |       if (this.step === 'processing' || this.step === 'paid') { | ||||||
|         this.moveToStep('success', true); |         this.moveToStep('success'); | ||||||
|       } else { // Edge case where the transaction gets accelerated by someone else or on another session
 |       } else { // Edge case where the transaction gets accelerated by someone else or on another session
 | ||||||
|         this.closeModal(); |         this.closeModal(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   moveToStep(step: CheckoutStep, force: boolean = false): void { |   moveToStep(step: CheckoutStep): void { | ||||||
|     if (this.isCheckoutLocked > 0 && !force) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     this.processing = false; |     this.processing = false; | ||||||
|     this._step = step; |     this._step = step; | ||||||
|     if (this.timeoutTimer) { |     if (this.timeoutTimer) { | ||||||
|       clearTimeout(this.timeoutTimer); |       clearTimeout(this.timeoutTimer); | ||||||
|     } |     } | ||||||
|     if (!this.estimate && ['quote', 'summary', 'checkout', 'processing'].includes(this.step)) { |     if (!this.estimate && ['quote', 'summary', 'checkout'].includes(this.step)) { | ||||||
|       this.fetchEstimate(); |       this.fetchEstimate(); | ||||||
|     } |     } | ||||||
|     if (this._step === 'checkout') { |     if (this._step === 'checkout') { | ||||||
| @ -220,9 +214,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|     } |     } | ||||||
|     if (this._step === 'checkout' && this.canPayWithBitcoin) { |     if (this._step === 'checkout' && this.canPayWithBitcoin) { | ||||||
|       this.btcpayInvoiceFailed = false; |       this.btcpayInvoiceFailed = false; | ||||||
|  |       this.loadingBtcpayInvoice = true; | ||||||
|       this.invoice = null; |       this.invoice = null; | ||||||
|       this.requestBTCPayInvoice(); |       this.requestBTCPayInvoice(); | ||||||
|     } else if (this._step === 'cashapp') { |     } else if (this._step === 'cashapp' && this.cashappEnabled) { | ||||||
|       this.loadingCashapp = true; |       this.loadingCashapp = true; | ||||||
|       this.setupSquare(); |       this.setupSquare(); | ||||||
|       this.scrollToElementWithTimeout('confirm-title', 'center', 100); |       this.scrollToElementWithTimeout('confirm-title', 'center', 100); | ||||||
| @ -234,10 +229,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|       this.loadingGooglePay = true; |       this.loadingGooglePay = true; | ||||||
|       this.setupSquare(); |       this.setupSquare(); | ||||||
|       this.scrollToElementWithTimeout('confirm-title', 'center', 100); |       this.scrollToElementWithTimeout('confirm-title', 'center', 100); | ||||||
|     } else if (this._step === 'cardonfile' && this.cardOnFileEnabled) { |  | ||||||
|       this.loadingCardOnFile = true; |  | ||||||
|       this.setupSquare(); |  | ||||||
|       this.scrollToElementWithTimeout('confirm-title', 'center', 100); |  | ||||||
|     } else if (this._step === 'paid') { |     } else if (this._step === 'paid') { | ||||||
|       this.timePaid = Date.now(); |       this.timePaid = Date.now(); | ||||||
|       this.timeoutTimer = setTimeout(() => { |       this.timeoutTimer = setTimeout(() => { | ||||||
| @ -251,7 +242,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|   closeModal(): void { |   closeModal(): void { | ||||||
|     this.completed.emit(true); |     this.completed.emit(true); | ||||||
|     this.moveToStep('summary', true); |     this.moveToStep('summary'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
| @ -332,6 +323,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           if (this.step === 'checkout' && this.canPayWithBitcoin && !this.loadingBtcpayInvoice) { |           if (this.step === 'checkout' && this.canPayWithBitcoin && !this.loadingBtcpayInvoice) { | ||||||
|  |             this.loadingBtcpayInvoice = true; | ||||||
|             this.requestBTCPayInvoice(); |             this.requestBTCPayInvoice(); | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
| @ -401,7 +393,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|         this.audioService.playSound('ascend-chime-cartoon'); |         this.audioService.playSound('ascend-chime-cartoon'); | ||||||
|         this.showSuccess = true; |         this.showSuccess = true; | ||||||
|         this.estimateSubscription.unsubscribe(); |         this.estimateSubscription.unsubscribe(); | ||||||
|         this.moveToStep('paid', true); |         this.moveToStep('paid'); | ||||||
|       }, |       }, | ||||||
|       error: (response) => { |       error: (response) => { | ||||||
|         this.processing = false; |         this.processing = false; | ||||||
| @ -457,8 +449,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|             await this.requestApplePayPayment(); |             await this.requestApplePayPayment(); | ||||||
|           } else if (this._step === 'googlepay') { |           } else if (this._step === 'googlepay') { | ||||||
|             await this.requestGooglePayPayment(); |             await this.requestGooglePayPayment(); | ||||||
|           } else if (this._step === 'cardonfile') { |  | ||||||
|             this.loadingCardOnFile = false; |  | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         error: () => { |         error: () => { | ||||||
| @ -513,14 +503,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|           } |           } | ||||||
|           this.loadingApplePay = false; |           this.loadingApplePay = false; | ||||||
|           applePayButton.addEventListener('click', async event => { |           applePayButton.addEventListener('click', async event => { | ||||||
|             if (this.isCheckoutLocked > 0 || this.isTokenizing > 0) { |  | ||||||
|               return; |  | ||||||
|             } |  | ||||||
|             event.preventDefault(); |             event.preventDefault(); | ||||||
|             try { |  | ||||||
|               // lock the checkout UI and show a loading spinner until the square modals are finished
 |  | ||||||
|               this.isCheckoutLocked++; |  | ||||||
|               this.isTokenizing++; |  | ||||||
|             const tokenResult = await this.applePay.tokenize(); |             const tokenResult = await this.applePay.tokenize(); | ||||||
|             if (tokenResult?.status === 'OK') { |             if (tokenResult?.status === 'OK') { | ||||||
|               const card = tokenResult.details?.card; |               const card = tokenResult.details?.card; | ||||||
| @ -531,9 +514,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|                 return; |                 return; | ||||||
|               } |               } | ||||||
|               const cardTag = md5(`${card.brand}${card.expMonth}${card.expYear}${card.last4}`.toLowerCase()); |               const cardTag = md5(`${card.brand}${card.expMonth}${card.expYear}${card.last4}`.toLowerCase()); | ||||||
|                 // keep checkout in loading state until the acceleration request completes
 |  | ||||||
|                 this.isTokenizing++; |  | ||||||
|                 this.isCheckoutLocked++; |  | ||||||
|               this.servicesApiService.accelerateWithApplePay$( |               this.servicesApiService.accelerateWithApplePay$( | ||||||
|                 this.tx.txid, |                 this.tx.txid, | ||||||
|                 tokenResult.token, |                 tokenResult.token, | ||||||
| @ -549,9 +529,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|                     this.applePay.destroy(); |                     this.applePay.destroy(); | ||||||
|                   } |                   } | ||||||
|                   setTimeout(() => { |                   setTimeout(() => { | ||||||
|                       this.isTokenizing--; |                     this.moveToStep('paid'); | ||||||
|                       this.isCheckoutLocked--; |  | ||||||
|                       this.moveToStep('paid', true); |  | ||||||
|                   }, 1000); |                   }, 1000); | ||||||
|                 }, |                 }, | ||||||
|                 error: (response) => { |                 error: (response) => { | ||||||
| @ -559,12 +537,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|                   this.accelerateError = response.error; |                   this.accelerateError = response.error; | ||||||
|                   if (!(response.status === 403 && response.error === 'not_available')) { |                   if (!(response.status === 403 && response.error === 'not_available')) { | ||||||
|                     setTimeout(() => { |                     setTimeout(() => { | ||||||
|                         this.isTokenizing--; |  | ||||||
|                         this.isCheckoutLocked--; |  | ||||||
|                       // Reset everything by reloading the page :D, can be improved
 |                       // Reset everything by reloading the page :D, can be improved
 | ||||||
|                       const urlParams = new URLSearchParams(window.location.search); |                       const urlParams = new URLSearchParams(window.location.search); | ||||||
|                       window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); |                       window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); | ||||||
|                       }, 10000); |                     }, 3000); | ||||||
|                   } |                   } | ||||||
|                 } |                 } | ||||||
|               }); |               }); | ||||||
| @ -578,11 +554,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|               } |               } | ||||||
|               throw new Error(errorMessage); |               throw new Error(errorMessage); | ||||||
|             } |             } | ||||||
|             } finally { |  | ||||||
|               // always unlock the checkout once we're finished
 |  | ||||||
|               this.isTokenizing--; |  | ||||||
|               this.isCheckoutLocked--; |  | ||||||
|             } |  | ||||||
|           }); |           }); | ||||||
|         } catch (e) { |         } catch (e) { | ||||||
|           this.processing = false; |           this.processing = false; | ||||||
| @ -631,14 +602,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|         this.loadingGooglePay = false; |         this.loadingGooglePay = false; | ||||||
| 
 | 
 | ||||||
|         document.getElementById('google-pay-button').addEventListener('click', async event => { |         document.getElementById('google-pay-button').addEventListener('click', async event => { | ||||||
|           if (this.isCheckoutLocked > 0 || this.isTokenizing > 0) { |  | ||||||
|             return; |  | ||||||
|           } |  | ||||||
|           event.preventDefault(); |           event.preventDefault(); | ||||||
|           try { |  | ||||||
|             // lock the checkout UI and show a loading spinner until the square modals are finished
 |  | ||||||
|             this.isCheckoutLocked++; |  | ||||||
|             this.isTokenizing++; |  | ||||||
|           const tokenResult = await this.googlePay.tokenize(); |           const tokenResult = await this.googlePay.tokenize(); | ||||||
|           if (tokenResult?.status === 'OK') { |           if (tokenResult?.status === 'OK') { | ||||||
|             const card = tokenResult.details?.card; |             const card = tokenResult.details?.card; | ||||||
| @ -656,9 +620,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|               return; |               return; | ||||||
|             } |             } | ||||||
|             const cardTag = md5(`${card.brand}${card.expMonth}${card.expYear}${card.last4}`.toLowerCase()); |             const cardTag = md5(`${card.brand}${card.expMonth}${card.expYear}${card.last4}`.toLowerCase()); | ||||||
|               // keep checkout in loading state until the acceleration request completes
 |  | ||||||
|               this.isCheckoutLocked++; |  | ||||||
|               this.isTokenizing++; |  | ||||||
|             this.servicesApiService.accelerateWithGooglePay$( |             this.servicesApiService.accelerateWithGooglePay$( | ||||||
|               this.tx.txid, |               this.tx.txid, | ||||||
|               tokenResult.token, |               tokenResult.token, | ||||||
| @ -676,22 +637,18 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|                   this.googlePay.destroy(); |                   this.googlePay.destroy(); | ||||||
|                 } |                 } | ||||||
|                 setTimeout(() => { |                 setTimeout(() => { | ||||||
|                     this.isTokenizing--; |                   this.moveToStep('paid'); | ||||||
|                     this.isCheckoutLocked--; |  | ||||||
|                     this.moveToStep('paid', true); |  | ||||||
|                 }, 1000); |                 }, 1000); | ||||||
|               }, |               }, | ||||||
|               error: (response) => { |               error: (response) => { | ||||||
|                 this.processing = false; |                 this.processing = false; | ||||||
|                 this.accelerateError = response.error; |                 this.accelerateError = response.error; | ||||||
|                   this.isTokenizing--; |  | ||||||
|                   this.isCheckoutLocked--; |  | ||||||
|                 if (!(response.status === 403 && response.error === 'not_available')) { |                 if (!(response.status === 403 && response.error === 'not_available')) { | ||||||
|                   setTimeout(() => { |                   setTimeout(() => { | ||||||
|                     // Reset everything by reloading the page :D, can be improved
 |                     // Reset everything by reloading the page :D, can be improved
 | ||||||
|                     const urlParams = new URLSearchParams(window.location.search); |                     const urlParams = new URLSearchParams(window.location.search); | ||||||
|                     window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); |                     window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); | ||||||
|                     }, 10000); |                   }, 3000); | ||||||
|                 } |                 } | ||||||
|               } |               } | ||||||
|             }); |             }); | ||||||
| @ -705,119 +662,11 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|             } |             } | ||||||
|             throw new Error(errorMessage); |             throw new Error(errorMessage); | ||||||
|           } |           } | ||||||
|           } finally { |  | ||||||
|             // always unlock the checkout once we're finished
 |  | ||||||
|             this.isTokenizing--; |  | ||||||
|             this.isCheckoutLocked--; |  | ||||||
|           } |  | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |  | ||||||
|    * Card On File |  | ||||||
|    */ |  | ||||||
|   async requestCardOnFilePayment(): Promise<void> { |  | ||||||
|     if (this.processing) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     if (this.conversionsSubscription) { |  | ||||||
|       this.conversionsSubscription.unsubscribe(); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     this.processing = true; |  | ||||||
|     this.conversionsSubscription = this.stateService.conversions$.subscribe( |  | ||||||
|       async (conversions) => { |  | ||||||
|         this.conversions = conversions; |  | ||||||
| 
 |  | ||||||
|         const costUSD = this.cost / 100_000_000 * conversions.USD; |  | ||||||
|         if (this.isCheckoutLocked > 0) { |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
|         const cardOnFile = this.estimate?.availablePaymentMethods?.cardOnFile; |  | ||||||
|         if (!cardOnFile?.card) { |  | ||||||
|           this.accelerateError = 'card_on_file_not_found'; |  | ||||||
|           return; |  | ||||||
|         } |  | ||||||
|         this.loadingCardOnFile = false; |  | ||||||
|          |  | ||||||
|         try { |  | ||||||
|           this.isCheckoutLocked += 2; |  | ||||||
|           this.isTokenizing += 2; |  | ||||||
|            |  | ||||||
|           const nameParts = cardOnFile.card.name.split(' '); |  | ||||||
|           const assumedGivenName = nameParts[0]; |  | ||||||
|           const assumedFamilyName = nameParts.length > 1 ? nameParts[1] : undefined; |  | ||||||
|           const verificationDetails = { |  | ||||||
|             card: { |  | ||||||
|               billing: { |  | ||||||
|                 givenName: assumedGivenName, |  | ||||||
|                 familyName: assumedFamilyName, |  | ||||||
|                 addressLines: [cardOnFile.card.billing.addressLine1 ?? ''], |  | ||||||
|                 city: cardOnFile.card.billing.locality ?? '', |  | ||||||
|                 state: cardOnFile.card.billing.administrativeDistrictLevel1 ?? '', |  | ||||||
|                 countyCode: cardOnFile.card.billing.country, |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           }; |  | ||||||
|           const verificationToken = await this.$verifyBuyer(this.payments, cardOnFile.card.card_id, verificationDetails, costUSD.toFixed(2)); |  | ||||||
|           if (!verificationToken || !verificationToken.token) { |  | ||||||
|             console.error(`SCA verification failed`); |  | ||||||
|             this.accelerateError = 'SCA Verification Failed. Payment Declined.'; |  | ||||||
|             this.processing = false; |  | ||||||
|             return; |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           this.servicesApiService.accelerateWithCardOnFile$( |  | ||||||
|             this.tx.txid, |  | ||||||
|             cardOnFile.card.card_id, |  | ||||||
|             verificationToken.token, |  | ||||||
|             `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, |  | ||||||
|             costUSD, |  | ||||||
|             verificationToken.userChallenged |  | ||||||
|           ).subscribe({ |  | ||||||
|             next: () => { |  | ||||||
|               this.processing = false; |  | ||||||
|               this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); |  | ||||||
|               this.audioService.playSound('ascend-chime-cartoon'); |  | ||||||
|               setTimeout(() => { |  | ||||||
|                 this.isCheckoutLocked--; |  | ||||||
|                 this.isTokenizing--; |  | ||||||
|                 this.moveToStep('paid', true); |  | ||||||
|               }, 1000); |  | ||||||
|             }, |  | ||||||
|             error: (response) => { |  | ||||||
|               this.processing = false; |  | ||||||
|               this.accelerateError = response.error; |  | ||||||
|               this.isCheckoutLocked--; |  | ||||||
|               this.isTokenizing--; |  | ||||||
|               if (!(response.status === 403 && response.error === 'not_available')) { |  | ||||||
|                 setTimeout(() => { |  | ||||||
|                   // Reset everything by reloading the page :D, can be improved
 |  | ||||||
|                   const urlParams = new URLSearchParams(window.location.search); |  | ||||||
|                   window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); |  | ||||||
|                 }, 3000); |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           }); |  | ||||||
| 
 |  | ||||||
|         } catch (e) { |  | ||||||
|           console.log(e); |  | ||||||
|           this.isCheckoutLocked--; |  | ||||||
|           this.isTokenizing--; |  | ||||||
|           this.processing = false; |  | ||||||
|           this.accelerateError = e.message; |  | ||||||
| 
 |  | ||||||
|         } finally { |  | ||||||
|           // always unlock the checkout once we're finished
 |  | ||||||
|           this.isCheckoutLocked--; |  | ||||||
|           this.isTokenizing--; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** |   /** | ||||||
|    * CASHAPP |    * CASHAPP | ||||||
|    */ |    */ | ||||||
| @ -838,7 +687,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const redirectHostname = document.location.hostname === 'localhost' ? `http://localhost:4200`: `https://${document.location.hostname}`; |         const redirectHostname = document.location.hostname === 'localhost' ? `http://localhost:4200`: `https://${document.location.hostname}`; | ||||||
|         const costUSD = this.cost / 100_000_000 * conversions.USD; |         const costUSD = this.step === 'processing' ? 69.69 : (this.cost / 100_000_000 * conversions.USD); // When we're redirected to this component, the payment data is already linked to the payment token, so does not matter what amonut we put in there, therefore it's 69.69
 | ||||||
|         const paymentRequest = this.payments.paymentRequest({ |         const paymentRequest = this.payments.paymentRequest({ | ||||||
|           countryCode: 'US', |           countryCode: 'US', | ||||||
|           currencyCode: 'USD', |           currencyCode: 'USD', | ||||||
| @ -878,7 +727,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|                   this.cashAppPay.destroy(); |                   this.cashAppPay.destroy(); | ||||||
|                 } |                 } | ||||||
|                 setTimeout(() => { |                 setTimeout(() => { | ||||||
|                   this.moveToStep('paid', true); |                   this.moveToStep('paid'); | ||||||
|                   if (window.history.replaceState) { |                   if (window.history.replaceState) { | ||||||
|                     const urlParams = new URLSearchParams(window.location.search); |                     const urlParams = new URLSearchParams(window.location.search); | ||||||
|                     window.history.replaceState(null, null, window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, '')); |                     window.history.replaceState(null, null, window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, '')); | ||||||
| @ -893,7 +742,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|                     // Reset everything by reloading the page :D, can be improved
 |                     // Reset everything by reloading the page :D, can be improved
 | ||||||
|                     const urlParams = new URLSearchParams(window.location.search); |                     const urlParams = new URLSearchParams(window.location.search); | ||||||
|                     window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); |                     window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); | ||||||
|                   }, 10000); |                   }, 3000); | ||||||
|                 } |                 } | ||||||
|               } |               } | ||||||
|             }); |             }); | ||||||
| @ -933,19 +782,16 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|    * BTCPay |    * BTCPay | ||||||
|    */ |    */ | ||||||
|   async requestBTCPayInvoice(): Promise<void> { |   async requestBTCPayInvoice(): Promise<void> { | ||||||
|     this.loadingBtcpayInvoice = true; |  | ||||||
|     this.servicesApiService.generateBTCPayAcceleratorInvoice$(this.tx.txid, this.userBid).pipe( |     this.servicesApiService.generateBTCPayAcceleratorInvoice$(this.tx.txid, this.userBid).pipe( | ||||||
|       switchMap(response => { |       switchMap(response => { | ||||||
|         return this.servicesApiService.retreiveInvoice$(response.btcpayInvoiceId); |         return this.servicesApiService.retreiveInvoice$(response.btcpayInvoiceId); | ||||||
|       }), |       }), | ||||||
|       catchError(error => { |       catchError(error => { | ||||||
|         console.log(error); |         console.log(error); | ||||||
|         this.loadingBtcpayInvoice = false; |  | ||||||
|         this.btcpayInvoiceFailed = true; |         this.btcpayInvoiceFailed = true; | ||||||
|         return of(null); |         return of(null); | ||||||
|       }) |       }) | ||||||
|     ).subscribe((invoice) => { |     ).subscribe((invoice) => { | ||||||
|         this.loadingBtcpayInvoice = false; |  | ||||||
|         this.invoice = invoice; |         this.invoice = invoice; | ||||||
|         this.cd.markForCheck(); |         this.cd.markForCheck(); | ||||||
|     }); |     }); | ||||||
| @ -955,7 +801,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|     this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); |     this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); | ||||||
|     this.audioService.playSound('ascend-chime-cartoon'); |     this.audioService.playSound('ascend-chime-cartoon'); | ||||||
|     this.estimateSubscription.unsubscribe(); |     this.estimateSubscription.unsubscribe(); | ||||||
|     this.moveToStep('paid', true); |     this.moveToStep('paid'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   isLoggedIn(): boolean { |   isLoggedIn(): boolean { | ||||||
| @ -982,6 +828,9 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get couldPayWithCashapp(): boolean { |   get couldPayWithCashapp(): boolean { | ||||||
|  |     if (!this.cashappEnabled) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|     return !!this.estimate?.availablePaymentMethods?.cashapp; |     return !!this.estimate?.availablePaymentMethods?.cashapp; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -1016,7 +865,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get canPayWithCashapp(): boolean { |   get canPayWithCashapp(): boolean { | ||||||
|     if (!this.conversions || (!this.isProdDomain && !isDevMode())) { |     if (!this.cashappEnabled || !this.conversions || (!this.isProdDomain && !isDevMode())) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -1063,22 +912,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get canPayWithCardOnFile(): boolean { |  | ||||||
|     if (!this.cardOnFileEnabled || !this.conversions || (!this.isProdDomain && !isDevMode())) { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const paymentMethod = this.estimate?.availablePaymentMethods?.cardOnFile; |  | ||||||
|     if (paymentMethod) { |  | ||||||
|       const costUSD = (this.cost / 100_000_000 * this.conversions.USD); |  | ||||||
|       if (costUSD >= paymentMethod.min && costUSD <= paymentMethod.max) { |  | ||||||
|         return true; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   get canPayWithBalance(): boolean { |   get canPayWithBalance(): boolean { | ||||||
|     if (!this.hasAccessToBalanceMode) { |     if (!this.hasAccessToBalanceMode) { | ||||||
|       return false; |       return false; | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
|           <th class="time text-right" i18n="accelerator.requested">Requested</th> |           <th class="time text-right" i18n="accelerator.requested">Requested</th> | ||||||
|         </ng-container> |         </ng-container> | ||||||
|         <ng-container *ngIf="!pending"> |         <ng-container *ngIf="!pending"> | ||||||
|           <th class="fee text-right text-truncate" i18n="transaction.bid-boost|Bid Boost">Bid Boost</th> |           <th class="fee text-right" i18n="transaction.bid-boost|Bid Boost">Bid Boost</th> | ||||||
|           <th class="block text-right" i18n="shared.block-title">Block</th> |           <th class="block text-right" i18n="shared.block-title">Block</th> | ||||||
|           <th class="pool text-right" i18n="mining.pool-name" *ngIf="!this.widget">Pool</th> |           <th class="pool text-right" i18n="mining.pool-name" *ngIf="!this.widget">Pool</th> | ||||||
|           <th class="status text-right" i18n="transaction.status|Transaction Status">Status</th> |           <th class="status text-right" i18n="transaction.status|Transaction Status">Status</th> | ||||||
| @ -64,8 +64,7 @@ | |||||||
|               <span *ngIf="acceleration.status === 'accelerating'" class="badge badge-warning" i18n="accelerator.pending">Pending</span> |               <span *ngIf="acceleration.status === 'accelerating'" class="badge badge-warning" i18n="accelerator.pending">Pending</span> | ||||||
|               <span *ngIf="acceleration.status.includes('completed') && acceleration.minedByPoolUniqueId && pools[acceleration.minedByPoolUniqueId]" class="badge badge-success"><ng-container i18n="accelerator.completed">Completed</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span> |               <span *ngIf="acceleration.status.includes('completed') && acceleration.minedByPoolUniqueId && pools[acceleration.minedByPoolUniqueId]" class="badge badge-success"><ng-container i18n="accelerator.completed">Completed</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span> | ||||||
|               <span *ngIf="acceleration.status.includes('completed') && (!acceleration.minedByPoolUniqueId || !pools[acceleration.minedByPoolUniqueId])" class="badge badge-success"><ng-container i18n="transaction.rbf.mined">Mined</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span> |               <span *ngIf="acceleration.status.includes('completed') && (!acceleration.minedByPoolUniqueId || !pools[acceleration.minedByPoolUniqueId])" class="badge badge-success"><ng-container i18n="transaction.rbf.mined">Mined</ng-container><span *ngIf="acceleration.status === 'completed_provisional'"> ⌛</span></span> | ||||||
|               <span *ngIf="acceleration.status.includes('failed') && acceleration.canceled" class="badge badge-danger"><ng-container i18n="accelerator.canceled">Canceled</ng-container><span *ngIf="acceleration.status === 'failed_provisional'"> ⌛</span></span> |               <span *ngIf="acceleration.status.includes('failed')" class="badge badge-danger"><ng-container i18n="accelerator.canceled">Canceled</ng-container><span *ngIf="acceleration.status === 'failed_provisional'"> ⌛</span></span> | ||||||
|               <span *ngIf="acceleration.status.includes('failed') && !acceleration.canceled" class="badge badge-danger"><ng-container i18n="accelerator.canceled">Failed</ng-container><span *ngIf="acceleration.status === 'failed_provisional'"> ⌛</span></span> |  | ||||||
|             </td> |             </td> | ||||||
|             <td class="date text-right" *ngIf="!this.widget"> |             <td class="date text-right" *ngIf="!this.widget"> | ||||||
|               <app-time kind="since" [time]="acceleration.added" [fastRender]="true" [showTooltip]="true"></app-time> |               <app-time kind="since" [time]="acceleration.added" [fastRender]="true" [showTooltip]="true"></app-time> | ||||||
|  | |||||||
| @ -478,30 +478,25 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   extendSummary(summary) { |   extendSummary(summary) { | ||||||
|     const extendedSummary = summary.slice(); |     let extendedSummary = summary.slice(); | ||||||
| 
 | 
 | ||||||
|     // Add a point at today's date to make the graph end at the current time
 |     // Add a point at today's date to make the graph end at the current time
 | ||||||
|     extendedSummary.unshift({ time: Date.now() / 1000, value: 0 }); |     extendedSummary.unshift({ time: Date.now() / 1000, value: 0 }); | ||||||
|  |     extendedSummary.reverse(); | ||||||
| 
 | 
 | ||||||
|     let maxTime = Date.now() / 1000; |     let oneHour = 60 * 60; | ||||||
| 
 |  | ||||||
|     const oneHour = 60 * 60; |  | ||||||
|     // Fill gaps longer than interval
 |     // Fill gaps longer than interval
 | ||||||
|     for (let i = 0; i < extendedSummary.length - 1; i++) { |     for (let i = 0; i < extendedSummary.length - 1; i++) { | ||||||
|       if (extendedSummary[i].time > maxTime) { |       let hours = Math.floor((extendedSummary[i + 1].time - extendedSummary[i].time) / oneHour);       | ||||||
|         extendedSummary[i].time = maxTime - 30; |  | ||||||
|       } |  | ||||||
|       maxTime = extendedSummary[i].time; |  | ||||||
|       const hours = Math.floor((extendedSummary[i].time - extendedSummary[i + 1].time) / oneHour); |  | ||||||
|       if (hours > 1) { |       if (hours > 1) { | ||||||
|         for (let j = 1; j < hours; j++) { |         for (let j = 1; j < hours; j++) { | ||||||
|           const newTime = extendedSummary[i].time - oneHour * j; |           let newTime = extendedSummary[i].time + oneHour * j; | ||||||
|           extendedSummary.splice(i + j, 0, { time: newTime, value: 0 }); |           extendedSummary.splice(i + j, 0, { time: newTime, value: 0 }); | ||||||
|         } |         } | ||||||
|         i += hours - 1; |         i += hours - 1; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return extendedSummary; |     return extendedSummary.reverse(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ export class AppComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|   @HostListener('document:keydown', ['$event']) |   @HostListener('document:keydown', ['$event']) | ||||||
|   handleKeyboardEvents(event: KeyboardEvent) { |   handleKeyboardEvents(event: KeyboardEvent) { | ||||||
|     if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) { |     if (event.target instanceof HTMLInputElement) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     // prevent arrow key horizontal scrolling
 |     // prevent arrow key horizontal scrolling
 | ||||||
|  | |||||||
| @ -10,10 +10,6 @@ | |||||||
|     </span> |     </span> | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   <div class="d-flex justify-content-center"> |  | ||||||
|     <app-mempool-error *ngIf="paymentErrorMessage" [error]="paymentErrorMessage"></app-mempool-error> |  | ||||||
|   </div> |  | ||||||
| 
 |  | ||||||
|   <div *ngIf="paymentStatus === 2"> |   <div *ngIf="paymentStatus === 2"> | ||||||
|      |      | ||||||
|     <form [formGroup]="paymentForm"> |     <form [formGroup]="paymentForm"> | ||||||
|  | |||||||
| @ -1,8 +1,9 @@ | |||||||
| import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; | import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; | ||||||
| import { FormBuilder, FormGroup } from '@angular/forms'; | import { FormBuilder, FormGroup } from '@angular/forms'; | ||||||
| import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | ||||||
| import { Subscription, of, catchError } from 'rxjs'; | import { ActivatedRoute } from '@angular/router'; | ||||||
| import { retry, tap } from 'rxjs/operators'; | import { Subscription, of, timer } from 'rxjs'; | ||||||
|  | import { filter, repeat, retry, switchMap, take, tap } from 'rxjs/operators'; | ||||||
| import { ServicesApiServices } from '@app/services/services-api.service'; | import { ServicesApiServices } from '@app/services/services-api.service'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
| @ -17,17 +18,30 @@ export class BitcoinInvoiceComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|   @Output() completed = new EventEmitter(); |   @Output() completed = new EventEmitter(); | ||||||
| 
 | 
 | ||||||
|   paymentForm: FormGroup; |   paymentForm: FormGroup; | ||||||
|  |   requestSubscription: Subscription | undefined; | ||||||
|   paymentStatusSubscription: Subscription | undefined; |   paymentStatusSubscription: Subscription | undefined; | ||||||
|   paymentStatus = 1; // 1 - Waiting for invoice | 2 - Pending payment | 3 - Payment completed
 |   paymentStatus = 1; // 1 - Waiting for invoice | 2 - Pending payment | 3 - Payment completed
 | ||||||
|   paymentErrorMessage: string = ''; |   paramMapSubscription: Subscription | undefined; | ||||||
|  |   invoiceSubscription: Subscription | undefined; | ||||||
|  |   invoiceTimeout; // Wait for angular to load all the things before making a request
 | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     private formBuilder: FormBuilder, |     private formBuilder: FormBuilder, | ||||||
|     private apiService: ServicesApiServices, |     private apiService: ServicesApiServices, | ||||||
|     private sanitizer: DomSanitizer |     private sanitizer: DomSanitizer, | ||||||
|  |     private activatedRoute: ActivatedRoute | ||||||
|   ) { } |   ) { } | ||||||
| 
 | 
 | ||||||
|   ngOnDestroy() { |   ngOnDestroy() { | ||||||
|  |     if (this.requestSubscription) { | ||||||
|  |       this.requestSubscription.unsubscribe(); | ||||||
|  |     } | ||||||
|  |     if (this.paramMapSubscription) { | ||||||
|  |       this.paramMapSubscription.unsubscribe(); | ||||||
|  |     } | ||||||
|  |     if (this.invoiceSubscription) { | ||||||
|  |       this.invoiceSubscription.unsubscribe(); | ||||||
|  |     } | ||||||
|     if (this.paymentStatusSubscription) { |     if (this.paymentStatusSubscription) { | ||||||
|       this.paymentStatusSubscription.unsubscribe(); |       this.paymentStatusSubscription.unsubscribe(); | ||||||
|     } |     } | ||||||
| @ -58,39 +72,15 @@ export class BitcoinInvoiceComponent implements OnInit, OnChanges, OnDestroy { | |||||||
|     } else { |     } else { | ||||||
|       this.paymentStatus = 4; |       this.paymentStatus = 4; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     this.monitorPendingInvoice(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   monitorPendingInvoice(): void { |  | ||||||
|     if (!this.invoice) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     if (this.paymentStatusSubscription) { |  | ||||||
|       this.paymentStatusSubscription.unsubscribe(); |  | ||||||
|     } |  | ||||||
|     this.paymentStatusSubscription = this.apiService.getPaymentStatus$(this.invoice.btcpayInvoiceId).pipe( |     this.paymentStatusSubscription = this.apiService.getPaymentStatus$(this.invoice.btcpayInvoiceId).pipe( | ||||||
|       tap(result => { |       retry({ delay: () => timer(2000)}), | ||||||
|         if (result.status === 204) { // Manually trigger an error in that case so we can retry
 |       repeat({delay: 2000}), | ||||||
|           throw result; |       filter((response) => response.status !== 204 && response.status !== 404), | ||||||
|         } else if (result.status === 200) { // Invoice settled
 |       take(1), | ||||||
|  |     ).subscribe(() => { | ||||||
|       this.paymentStatus = 3; |       this.paymentStatus = 3; | ||||||
|       this.completed.emit(); |       this.completed.emit(); | ||||||
|         } |     }); | ||||||
|       }), |  | ||||||
|       catchError(err => { |  | ||||||
|         if (err.status === 204 || err.status === 504) { |  | ||||||
|           throw err; // Will trigger the retry
 |  | ||||||
|         } else if (err.status === 400) { |  | ||||||
|           this.paymentErrorMessage = 'Invoice has expired'; |  | ||||||
|         } else if (err.status === 404) { |  | ||||||
|           this.paymentErrorMessage = 'Invoice is no longer valid'; |  | ||||||
|         } |  | ||||||
|         this.paymentStatus = -1; |  | ||||||
|         return of(null); |  | ||||||
|       }), |  | ||||||
|       retry({ delay: 1000 }), |  | ||||||
|     ).subscribe(); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get availableMethods(): string[] { |   get availableMethods(): string[] { | ||||||
|  | |||||||
| @ -281,11 +281,9 @@ | |||||||
|           <div class="col" style="max-height: 410px" [style.order]="isMobile && widget.mobileOrder || 8"> |           <div class="col" style="max-height: 410px" [style.order]="isMobile && widget.mobileOrder || 8"> | ||||||
|             <div class="card"> |             <div class="card"> | ||||||
|               <div class="card-body"> |               <div class="card-body"> | ||||||
|                 <a class="title-link mb-0" style="margin-top: -2px" href="" [routerLink]="['/wallet/' + widget.props.wallet | relativeUrl]"> |                 <span class="title-link"> | ||||||
|                   <h5 class="card-title d-inline" i18n="dashboard.treasury-transactions">Treasury Transactions</h5> |                   <h5 class="card-title d-inline" i18n="dashboard.treasury-transactions">Treasury Transactions</h5> | ||||||
|                   <span> </span> |                 </span> | ||||||
|                   <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon> |  | ||||||
|                 </a> |  | ||||||
|                 <app-address-transactions-widget [addressSummary$]="walletSummary$"></app-address-transactions-widget> |                 <app-address-transactions-widget [addressSummary$]="walletSummary$"></app-address-transactions-widget> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|  | |||||||
| @ -19,10 +19,12 @@ | |||||||
|     } @else if (!user) { |     } @else if (!user) { | ||||||
|       <!-- User not logged in --> |       <!-- User not logged in --> | ||||||
|       <div class="alert alert-mempool d-block text-center w-100"> |       <div class="alert alert-mempool d-block text-center w-100"> | ||||||
|         <div class="d-inline align-middle pr-2"> |         <div class="d-inline align-middle"> | ||||||
|           <span>To use the faucet, please</span> |           <span>To use the faucet, please </span> | ||||||
|  |           <a routerLink="/login" [queryParams]="{'redirectTo': '/testnet4/faucet'}">login</a> | ||||||
|  |           <span class="mr-2"> or</span> | ||||||
|         </div> |         </div> | ||||||
|         <app-github-login customClass="btn btn-sm" width="150px" redirectTo="/testnet4/faucet" buttonString="Sign up with"></app-github-login> |         <app-twitter-login customClass="btn btn-sm" width="220px" redirectTo="/testnet4/faucet" buttonString="Sign up with Twitter"></app-twitter-login> | ||||||
|       </div> |       </div> | ||||||
|     } |     } | ||||||
|     @else if (user && user.status === 'pending' && !user.email && user.snsId) { |     @else if (user && user.status === 'pending' && !user.email && user.snsId) { | ||||||
| @ -34,18 +36,18 @@ | |||||||
|       </div> |       </div> | ||||||
|     } |     } | ||||||
|     @else if (error === 'not_available') { |     @else if (error === 'not_available') { | ||||||
|       <!-- User logged in but not a paid user or did not link its Github account --> |       <!-- User logged in but not a paid user or did not link its Twitter account --> | ||||||
|       <div class="alert alert-mempool d-block text-center w-100"> |       <div class="alert alert-mempool d-block text-center w-100"> | ||||||
|         <div class="d-inline align-middle"> |         <div class="d-inline align-middle"> | ||||||
|           <span class="mb-2 mr-2">To use the faucet, please</span> |           <span class="mb-2 mr-2">To use the faucet, please</span> | ||||||
|         </div> |         </div> | ||||||
|         <app-github-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your"></app-github-login> |         <app-twitter-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your Twitter"></app-twitter-login> | ||||||
|       </div> |       </div> | ||||||
|     } |     } | ||||||
|     @else if (error === 'account_limited') { |     @else if (error === 'account_limited') { | ||||||
|       <div class="alert alert-mempool d-block text-center w-100"> |       <div class="alert alert-mempool d-block text-center w-100"> | ||||||
|         <div class="d-inline align-middle"> |         <div class="d-inline align-middle"> | ||||||
|           <span class="mb-2 mr-2">Your account does not allow you to access the faucet</span> |           <span class="mb-2 mr-2">Your Twitter account does not allow you to access the faucet</span> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ export class FaucetComponent implements OnInit, OnDestroy { | |||||||
|     min: number; // minimum amount to request at once (in sats)
 |     min: number; // minimum amount to request at once (in sats)
 | ||||||
|     max: number; // maximum amount to request at once
 |     max: number; // maximum amount to request at once
 | ||||||
|     address?: string; // faucet address
 |     address?: string; // faucet address
 | ||||||
|     code: 'ok' | 'faucet_not_available' | 'faucet_maximum_reached' | 'faucet_too_soon' | 'faucet_not_available_no_utxo'; |     code: 'ok' | 'faucet_not_available' | 'faucet_maximum_reached' | 'faucet_too_soon'; | ||||||
|   } | null = null; |   } | null = null; | ||||||
|   faucetForm: FormGroup; |   faucetForm: FormGroup; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +0,0 @@ | |||||||
| <a href="#" (click)="githubLogin()" [class]="(disabled ? 'disabled': '') + (customClass ? customClass : 'w-100 btn mt-1 d-flex justify-content-center align-items-center')" style="background-color: rgb(31, 35, 40)" [style]="width ? 'width: ' + width : ''"> |  | ||||||
|   <span class="ml-2 text-light align-middle">{{ buttonString }}</span> |  | ||||||
|   <svg height="32" viewBox="0 0 18 16" width="32" style="fill: white; padding-left: 5px"> |  | ||||||
|     <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/> |  | ||||||
|   </svg> |  | ||||||
| </a> |  | ||||||
| @ -1,25 +0,0 @@ | |||||||
| import { Component, EventEmitter, Input, Output } from '@angular/core'; |  | ||||||
| @Component({ |  | ||||||
|   selector: 'app-github-login', |  | ||||||
|   templateUrl: './github-login.component.html', |  | ||||||
| }) |  | ||||||
| export class GithubLogin { |  | ||||||
|   @Input() width: string | null = null; |  | ||||||
|   @Input() customClass: string | null = null; |  | ||||||
|   @Input() buttonString: string= 'unset'; |  | ||||||
|   @Input() redirectTo: string | null = null; |  | ||||||
|   @Output() clicked = new EventEmitter<boolean>(); |  | ||||||
|   @Input() disabled: boolean = false; |  | ||||||
| 
 |  | ||||||
|   constructor() {} |  | ||||||
| 
 |  | ||||||
|   githubLogin() { |  | ||||||
|     this.clicked.emit(true); |  | ||||||
|     if (this.redirectTo) { |  | ||||||
|       location.replace(`/api/v1/services/auth/login/github?redirectTo=${encodeURIComponent(this.redirectTo)}`); |  | ||||||
|     } else { |  | ||||||
|       location.replace(`/api/v1/services/auth/login/github?redirectTo=${location.href}`); |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -4,8 +4,9 @@ | |||||||
|   <nav class="navbar navbar-expand-md navbar-dark"> |   <nav class="navbar navbar-expand-md navbar-dark"> | ||||||
|   <!-- Hamburger --> |   <!-- Hamburger --> | ||||||
|   <ng-container *ngIf="servicesEnabled"> |   <ng-container *ngIf="servicesEnabled"> | ||||||
|     <div *ngIf="user" class="profile_image_container" (click)="hamburgerClick($event)"> |     <div *ngIf="user" class="profile_image_container" [class]="{'anon': !user.imageMd5}" (click)="hamburgerClick($event)"> | ||||||
|       <img [src]="'/api/v1/services/account/images/' + user.username" class="profile_image" onError="this.src = '/resources/anon.svg'; this.className = 'anon'" /> |       <img *ngIf="user.imageMd5" [src]="'/api/v1/services/account/images/' + user.username + '/md5=' + user.imageMd5" class="profile_image"> | ||||||
|  |       <app-svg-images style="color: lightgrey; fill: lightgray" *ngIf="!user.imageMd5" name="anon"></app-svg-images> | ||||||
|     </div> |     </div> | ||||||
|     <div *ngIf="false && user === null" class="profile_image_container" (click)="hamburgerClick($event)"> |     <div *ngIf="false && user === null" class="profile_image_container" (click)="hamburgerClick($event)"> | ||||||
|       <app-svg-images name="hamburger" height="40"></app-svg-images> |       <app-svg-images name="hamburger" height="40"></app-svg-images> | ||||||
| @ -22,7 +23,7 @@ | |||||||
|       } @else { |       } @else { | ||||||
|         <ng-template [ngIf]="subdomain && enterpriseInfo"> |         <ng-template [ngIf]="subdomain && enterpriseInfo"> | ||||||
|           <div class="subdomain_container"> |           <div class="subdomain_container"> | ||||||
|             <img [src]="enterpriseInfo.img || '/api/v1/services/enterprise/images/' + subdomain + '/logo'" class="subdomain_logo" [class]="{'rounded': enterpriseInfo.rounded_corner}"> |             <img [src]="enterpriseInfo.img || '/api/v1/services/enterprise/images/' + subdomain + '/logo?imageMd5=' + enterpriseInfo.imageMd5" class="subdomain_logo" [class]="{'rounded': enterpriseInfo.rounded_corner}"> | ||||||
|           </div> |           </div> | ||||||
|           <div class="vertical-line"></div> |           <div class="vertical-line"></div> | ||||||
|         </ng-template> |         </ng-template> | ||||||
| @ -42,7 +43,7 @@ | |||||||
|     } @else { |     } @else { | ||||||
|       <ng-template [ngIf]="subdomain && enterpriseInfo"> |       <ng-template [ngIf]="subdomain && enterpriseInfo"> | ||||||
|         <div class="subdomain_container"> |         <div class="subdomain_container"> | ||||||
|           <img [src]="enterpriseInfo.img || '/api/v1/services/enterprise/images/' + subdomain + '/logo'" class="subdomain_logo" [class]="{'rounded': enterpriseInfo.rounded_corner}"> |           <img [src]="enterpriseInfo.img || '/api/v1/services/enterprise/images/' + subdomain + '/logo?imageMd5=' + enterpriseInfo.imageMd5" class="subdomain_logo" [class]="{'rounded': enterpriseInfo.rounded_corner}"> | ||||||
|         </div> |         </div> | ||||||
|         <div class="vertical-line"></div> |         <div class="vertical-line"></div> | ||||||
|       </ng-template> |       </ng-template> | ||||||
|  | |||||||
| @ -269,7 +269,7 @@ nav { | |||||||
|   text-align: center; |   text-align: center; | ||||||
|   align-self: center; |   align-self: center; | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
|   .anon { |   &.anon { | ||||||
|     border: 1.5px solid lightgrey; |     border: 1.5px solid lightgrey; | ||||||
|     color: lightgrey; |     color: lightgrey; | ||||||
|     border-radius: 5px; |     border-radius: 5px; | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
|       <h1 class="m-0 pt-1 pt-md-0">{{ poolStats.pool.name }}</h1> |       <h1 class="m-0 pt-1 pt-md-0">{{ poolStats.pool.name }}</h1> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <div class="box pool-details"> |     <div class="box"> | ||||||
|       <div class="row"> |       <div class="row"> | ||||||
| 
 | 
 | ||||||
|         <div class="col-lg-6"> |         <div class="col-lg-6"> | ||||||
| @ -173,125 +173,7 @@ | |||||||
|     <div class="spinner-border text-light"></div> |     <div class="spinner-border text-light"></div> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <!-- Stratum Job --> |  | ||||||
|   <ng-container *ngIf="(job$ | async) as job;"> |  | ||||||
|     <h2 i18n="pool.next_block">Next block</h2> |  | ||||||
|     <div class="box mb-3"> |  | ||||||
|       <div class="row" > |  | ||||||
|         <div class="col"> |  | ||||||
|           <table class="table table-borderless table-striped"> |  | ||||||
|             <tbody> |  | ||||||
|               <tr> |  | ||||||
|                 <td> |  | ||||||
|                   <table class="job-table table table-xs table-borderless table-fixed table-data"> |  | ||||||
|                     <thead> |  | ||||||
|                       <tr> |  | ||||||
|                         <th class="data-title clip text-center height" i18n="latest-blocks.height">Height</th> |  | ||||||
|                         <th class="data-title clip text-center expected" i18n="next-block.expected-time">Expected</th> |  | ||||||
|                         <th class="data-title clip text-center reward" i18n="latest-blocks.reward">Reward</th> |  | ||||||
|                         <th class="data-title clip text-center timestamp" i18n="next-block.timestamp">Timestamp</th> |  | ||||||
|                       </tr> |  | ||||||
|                     </thead> |  | ||||||
|                     <tbody> |  | ||||||
|                       <tr> |  | ||||||
|                         <td class="text-center height"> |  | ||||||
|                           {{ job.height }} |  | ||||||
|                         </td> |  | ||||||
|                         <td class="text-center expected"> |  | ||||||
|                           <ng-container *ngIf="(expectedBlockTime$ | async) as expectedBlockTime; else expectedPlaceholder"> |  | ||||||
|                             <app-time kind="until" [time]="expectedBlockTime" [fastRender]="false" [fixedRender]="true" [precision]="1" minUnit="minute"></app-time> |  | ||||||
|                           </ng-container> |  | ||||||
|                           <ng-template #expectedPlaceholder>~</ng-template> |  | ||||||
|                         </td> |  | ||||||
|                         <td class="text-center reward"> |  | ||||||
|                           <app-amount [satoshis]="job.reward"></app-amount> |  | ||||||
|                         </td> |  | ||||||
|                         <td class="text-center timestamp"> |  | ||||||
|                           <app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="job.timestamp" [precision]="1" minUnit="minute" [hideTimeSince]="true"></app-timestamp> |  | ||||||
|                         </td> |  | ||||||
|                       </tr> |  | ||||||
|                     </tbody> |  | ||||||
|                   </table> |  | ||||||
|                 </td> |  | ||||||
|               </tr> |  | ||||||
|               <tr> |  | ||||||
|                 <td> |  | ||||||
|                   <table class="job-table table table-xs table-borderless table-fixed table-data"> |  | ||||||
|                     <thead> |  | ||||||
|                       <tr> |  | ||||||
|                         <th class="data-title clip text-center coinbase" i18n="latest-blocks.coinbasetag">Coinbase tag</th> |  | ||||||
|                         <th class="data-title clip text-center clean" i18n="next-block.clean">Clean</th> |  | ||||||
|                         <th class="data-title clip text-center prevhash" i18n="next-block.prevhash">Prevhash</th> |  | ||||||
|                         <th class="data-title clip text-center job-received" i18n="next-block.job-received">Job Received</th> |  | ||||||
|                       </tr> |  | ||||||
|                     </thead> |  | ||||||
|                     <tbody> |  | ||||||
|                       <tr> |  | ||||||
|                         <td class="text-center coinbase"> |  | ||||||
|                           {{ job.scriptsig | hex2ascii }} |  | ||||||
|                         </td> |  | ||||||
|                         <td class="text-center clean"> |  | ||||||
|                           @if (job.cleanJobs) { |  | ||||||
|                             <fa-icon [icon]="['fas', 'check-circle']" [fixedWidth]="true"></fa-icon> |  | ||||||
|                           } @else { |  | ||||||
|                             <fa-icon [icon]="['fas', 'times-circle']" [fixedWidth]="true"></fa-icon> |  | ||||||
|                           } |  | ||||||
|                         </td> |  | ||||||
|                         <td class="text-center prevhash"> |  | ||||||
|                           <a [routerLink]="['/block' | relativeUrl, job.prevHash]"> |  | ||||||
|                             <app-truncate [text]="job.prevHash" [lastChars]="8"></app-truncate> |  | ||||||
|                           </a> |  | ||||||
|                         </td> |  | ||||||
|                         <td class="text-center job-received"> |  | ||||||
|                           <app-timestamp [customFormat]="'yyyy-MM-dd HH:mm:ss'" [unixTime]="job.received / 1000" [precision]="1" minUnit="minute" [hideTimeSince]="true"></app-timestamp> |  | ||||||
|                         </td> |  | ||||||
|                       </tr> |  | ||||||
|                     </tbody> |  | ||||||
|                   </table> |  | ||||||
|                 </td> |  | ||||||
|               </tr> |  | ||||||
|               <tr> |  | ||||||
|                 <td> |  | ||||||
|                   <table class="stratum-table"> |  | ||||||
|                     <thead> |  | ||||||
|                       <tr> |  | ||||||
|                         <th class="data-title clip text-center" [attr.colspan]="Math.max(job.merkleBranches.length, 12)"> |  | ||||||
|                           <a class="title-link" href="" [routerLink]="['/stratum' | relativeUrl]"> |  | ||||||
|                             Merkle Branches |  | ||||||
|                             <span> </span> |  | ||||||
|                             <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 13px; color: var(--title-fg)"></fa-icon> |  | ||||||
|                           </a> |  | ||||||
|                         </th> |  | ||||||
|                       </tr> |  | ||||||
|                     </thead> |  | ||||||
|                     <tbody> |  | ||||||
|                       <tr> |  | ||||||
|                         @for (branch of job.merkleBranches; track $index) { |  | ||||||
|                           <td class="merkle" [style.background-color]="branch ? '#' + branch.slice(0, 6) : ''"> |  | ||||||
|                             @if ($index === 0 && branch) { |  | ||||||
|                               <a [routerLink]="['/tx' | relativeUrl, reverseHash(branch)]" class="cell-link"> |  | ||||||
|                                 <fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true" style="vertical-align: text-top; font-size: 14px; color: white"></fa-icon> |  | ||||||
|                               </a> |  | ||||||
|                             } |  | ||||||
|                           </td> |  | ||||||
|                         } |  | ||||||
|                         @for (_ of [].constructor(Math.max(0, 12 - job.merkleBranches.length)); track $index) { |  | ||||||
|                           <td class="merkle empty-branch"></td> |  | ||||||
|                         } |  | ||||||
|                       </tr> |  | ||||||
|                     </tbody> |  | ||||||
|                   </table> |  | ||||||
|                 </td> |  | ||||||
|               </tr> |  | ||||||
|             </tbody> |  | ||||||
|           </table> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </div> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <!-- Blocks list --> |   <!-- Blocks list --> | ||||||
|   <h2 i18n="master-page.blocks">Blocks</h2> |  | ||||||
|   <table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" |   <table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" | ||||||
|     [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()"> |     [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()"> | ||||||
|     <ng-container *ngIf="blocks$ | async as blocks; else skeleton"> |     <ng-container *ngIf="blocks$ | async as blocks; else skeleton"> | ||||||
|  | |||||||
| @ -49,10 +49,12 @@ div.scrollable { | |||||||
|   max-height: 75px; |   max-height: 75px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .pool-details { | .box { | ||||||
|  |   padding-bottom: 5px; | ||||||
|   @media (min-width: 767.98px) { |   @media (min-width: 767.98px) { | ||||||
|     min-height: 187px; |     min-height: 187px; | ||||||
|   } |   } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| .label { | .label { | ||||||
|   width: 25%; |   width: 25%; | ||||||
| @ -153,7 +155,6 @@ div.scrollable { | |||||||
| 	width: auto; | 	width: auto; | ||||||
|   text-align: left; |   text-align: left; | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| .skeleton-loader { | .skeleton-loader { | ||||||
|   max-width: 200px; |   max-width: 200px; | ||||||
| @ -214,68 +215,3 @@ div.scrollable { | |||||||
| .taller-row { | .taller-row { | ||||||
|   height: 75px; |   height: 75px; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| .stratum-table { |  | ||||||
|   width: 100%; |  | ||||||
| 
 |  | ||||||
|   .merkle { |  | ||||||
|     width: 100px; |  | ||||||
|     text-align: center; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .empty-branch { |  | ||||||
|     outline: solid 1px white; |  | ||||||
|     outline-offset: -1px; |  | ||||||
| 
 |  | ||||||
|     &::after { |  | ||||||
|       content: ""; |  | ||||||
|       position: absolute; |  | ||||||
|       left: 0; |  | ||||||
|       top: 0; |  | ||||||
|       height: 100%; |  | ||||||
|       width: 100%; |  | ||||||
|       background: linear-gradient(to top left, transparent, transparent 48%, white 49%, white 51%, transparent 52%, transparent); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   td { |  | ||||||
|     position: relative; |  | ||||||
|     height: 2em; |  | ||||||
| 
 |  | ||||||
|     .cell-link { |  | ||||||
|       position: absolute; |  | ||||||
|       top: 0; |  | ||||||
|       left: 0; |  | ||||||
|       width: 100%; |  | ||||||
|       height: 100%; |  | ||||||
|       color: inherit; |  | ||||||
|       text-decoration: none; |  | ||||||
|       display: flex; |  | ||||||
|       justify-content: center; |  | ||||||
|       align-items: center; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .job-table { |  | ||||||
|   td, th { |  | ||||||
|     width: 25%; |  | ||||||
|     max-width: 25%; |  | ||||||
|     min-width: 25%; |  | ||||||
|     overflow: hidden; |  | ||||||
|     text-overflow: ellipsis; |  | ||||||
|     padding: 0.1rem 0.2rem; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @media (max-width: 767.98px) { |  | ||||||
|     .expected, .timestamp, .clean, .job-received { |  | ||||||
|       display: none; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .title-link, .title-link:hover, .title-link:focus, .title-link:active { |  | ||||||
|   display: block; |  | ||||||
|   text-decoration: none; |  | ||||||
|   color: inherit; |  | ||||||
| } |  | ||||||
| @ -10,9 +10,6 @@ import { selectPowerOfTen } from '@app/bitcoin.utils'; | |||||||
| import { formatNumber } from '@angular/common'; | import { formatNumber } from '@angular/common'; | ||||||
| import { SeoService } from '@app/services/seo.service'; | import { SeoService } from '@app/services/seo.service'; | ||||||
| import { HttpErrorResponse } from '@angular/common/http'; | import { HttpErrorResponse } from '@angular/common/http'; | ||||||
| import { StratumJob } from '../../interfaces/websocket.interface'; |  | ||||||
| import { WebsocketService } from '../../services/websocket.service'; |  | ||||||
| import { MiningService } from '../../services/mining.service'; |  | ||||||
| 
 | 
 | ||||||
| interface AccelerationTotal { | interface AccelerationTotal { | ||||||
|   cost: number, |   cost: number, | ||||||
| @ -30,16 +27,12 @@ export class PoolComponent implements OnInit { | |||||||
|   @Input() left: number | string = 75; |   @Input() left: number | string = 75; | ||||||
| 
 | 
 | ||||||
|   gfg = true; |   gfg = true; | ||||||
|   stratumEnabled = this.stateService.env.STRATUM_ENABLED; |  | ||||||
| 
 | 
 | ||||||
|   formatNumber = formatNumber; |   formatNumber = formatNumber; | ||||||
|   Math = Math; |  | ||||||
|   slugSubscription: Subscription; |   slugSubscription: Subscription; | ||||||
|   poolStats$: Observable<PoolStat>; |   poolStats$: Observable<PoolStat>; | ||||||
|   blocks$: Observable<BlockExtended[]>; |   blocks$: Observable<BlockExtended[]>; | ||||||
|   oobFees$: Observable<AccelerationTotal[]>; |   oobFees$: Observable<AccelerationTotal[]>; | ||||||
|   job$: Observable<StratumJob | null>; |  | ||||||
|   expectedBlockTime$: Observable<number>; |  | ||||||
|   isLoading = true; |   isLoading = true; | ||||||
|   error: HttpErrorResponse | null = null; |   error: HttpErrorResponse | null = null; | ||||||
| 
 | 
 | ||||||
| @ -60,8 +53,6 @@ export class PoolComponent implements OnInit { | |||||||
|     private apiService: ApiService, |     private apiService: ApiService, | ||||||
|     private route: ActivatedRoute, |     private route: ActivatedRoute, | ||||||
|     public stateService: StateService, |     public stateService: StateService, | ||||||
|     private websocketService: WebsocketService, |  | ||||||
|     private miningService: MiningService, |  | ||||||
|     private seoService: SeoService, |     private seoService: SeoService, | ||||||
|   ) { |   ) { | ||||||
|     this.auditAvailable = this.stateService.env.AUDIT; |     this.auditAvailable = this.stateService.env.AUDIT; | ||||||
| @ -138,31 +129,6 @@ export class PoolComponent implements OnInit { | |||||||
|       }), |       }), | ||||||
|       filter(oob => oob.length === 3 && oob[2].count > 0) |       filter(oob => oob.length === 3 && oob[2].count > 0) | ||||||
|     ); |     ); | ||||||
| 
 |  | ||||||
|     if (this.stratumEnabled) { |  | ||||||
|       this.job$ = combineLatest([ |  | ||||||
|         this.poolStats$.pipe( |  | ||||||
|           tap((poolStats) => { |  | ||||||
|             this.websocketService.startTrackStratum(poolStats.pool.unique_id); |  | ||||||
|           }) |  | ||||||
|         ), |  | ||||||
|         this.stateService.stratumJobs$ |  | ||||||
|       ]).pipe( |  | ||||||
|         map(([poolStats, jobs]) => { |  | ||||||
|           return jobs[poolStats.pool.unique_id]; |  | ||||||
|         }) |  | ||||||
|       ); |  | ||||||
| 
 |  | ||||||
|       this.expectedBlockTime$ = combineLatest([ |  | ||||||
|         this.miningService.getMiningStats('1w'), |  | ||||||
|         this.poolStats$, |  | ||||||
|         this.stateService.difficultyAdjustment$ |  | ||||||
|       ]).pipe( |  | ||||||
|         map(([miningStats, poolStat, da]) => { |  | ||||||
|           return (da.timeAvg / ((poolStat.estimatedHashrate || 0) / (miningStats.lastEstimatedHashrate * 1_000_000_000_000_000_000))) + Date.now() + da.timeOffset; |  | ||||||
|         }) |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   prepareChartOptions(hashrate, share) { |   prepareChartOptions(hashrate, share) { | ||||||
| @ -361,10 +327,6 @@ export class PoolComponent implements OnInit { | |||||||
|     return block.height; |     return block.height; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   reverseHash(hash: string) { |  | ||||||
|     return hash.match(/../g).reverse().join(''); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   ngOnDestroy(): void { |   ngOnDestroy(): void { | ||||||
|     this.slugSubscription.unsubscribe(); |     this.slugSubscription.unsubscribe(); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -82,10 +82,6 @@ export class ServerHealthComponent implements OnInit { | |||||||
|       return '🇺🇸'; |       return '🇺🇸'; | ||||||
|     } else if (host.includes('.va1.')) { |     } else if (host.includes('.va1.')) { | ||||||
|       return '🇺🇸'; |       return '🇺🇸'; | ||||||
|     } else if (host.includes('.sg1.')) { |  | ||||||
|       return '🇸🇬'; |  | ||||||
|     } else if (host.includes('.hnl.')) { |  | ||||||
|       return '🤙'; |  | ||||||
|     } else { |     } else { | ||||||
|       return ''; |       return ''; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,55 +0,0 @@ | |||||||
| <div class="container-xl" style="min-height: 335px"> |  | ||||||
|   <h1 class="float-left" i18n="master-page.blocks">Stratum Jobs</h1> |  | ||||||
| 
 |  | ||||||
|   <div class="clearfix"></div> |  | ||||||
| 
 |  | ||||||
|   <div style="min-height: 295px"> |  | ||||||
|     <table *ngIf="poolsReady && (rows$ | async) as rows;" class="stratum-table"> |  | ||||||
|       <thead> |  | ||||||
|         <tr> |  | ||||||
|           <td class="merkle" [attr.colspan]="rows[0]?.merkleCells?.length || 4"> |  | ||||||
|             Merkle Branches |  | ||||||
|           </td> |  | ||||||
|           <td class="pool">Pool</td> |  | ||||||
|           <td class="tag">Coinbase Tag</td> |  | ||||||
|           <td class="reward">Reward</td> |  | ||||||
|           <td class="height">Height</td> |  | ||||||
|         </tr> |  | ||||||
|       </thead> |  | ||||||
|       <tbody> |  | ||||||
|         @for (row of rows; track row.job.pool) { |  | ||||||
|           <tr> |  | ||||||
|             @for (cell of row.merkleCells; track $index) { |  | ||||||
|               <td class="merkle" [style.background-color]="cell.hash ? '#' + cell.hash.slice(0, 6) : ''"> |  | ||||||
|                 @if ($index === 0 && cell.hash) { |  | ||||||
|                   <a [routerLink]="['/tx' | relativeUrl, reverseHash(cell.hash)]" class="cell-link"> |  | ||||||
|                     <div class="pipe-segment" [class]="pipeToClass(cell.type)"></div> |  | ||||||
|                   </a> |  | ||||||
|                 } @else { |  | ||||||
|                   <div class="pipe-segment" [class]="pipeToClass(cell.type)"></div> |  | ||||||
|                 } |  | ||||||
|               </td> |  | ||||||
|             } |  | ||||||
|             <td class="pool"> |  | ||||||
|               @if (pools[row.job.pool]) { |  | ||||||
|                 <a class="badge" [routerLink]="[('/mining/pool/' + pools[row.job.pool].slug) | relativeUrl]"> |  | ||||||
|                   <img class="pool-logo" [src]="'/resources/mining-pools/' + pools[row.job.pool].slug + '.svg'" onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + pools[row.job.pool].name + ' mining pool'">  |  | ||||||
|                   {{ pools[row.job.pool].name}} |  | ||||||
|                 </a> |  | ||||||
|               } |  | ||||||
|             </td> |  | ||||||
|             <td class="tag"> |  | ||||||
|               {{ row.job.tag }} |  | ||||||
|             </td> |  | ||||||
|             <td class="reward"> |  | ||||||
|               <app-amount [satoshis]="row.job.reward"></app-amount> |  | ||||||
|             </td> |  | ||||||
|             <td class="height"> |  | ||||||
|               {{ row.job.height }} |  | ||||||
|             </td> |  | ||||||
|           </tr> |  | ||||||
|         } |  | ||||||
|       </tbody> |  | ||||||
|     </table> |  | ||||||
|   </div> |  | ||||||
| </div> |  | ||||||
| @ -1,138 +0,0 @@ | |||||||
| .stratum-table { |  | ||||||
|   width: 100%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| td { |  | ||||||
|   position: relative; |  | ||||||
|   height: 2em; |  | ||||||
| 
 |  | ||||||
|   &.height, &.reward, &.tag { |  | ||||||
|     padding: 0 5px; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   &.tag { |  | ||||||
|     max-width: 180px; |  | ||||||
|     overflow: hidden; |  | ||||||
|     text-overflow: ellipsis; |  | ||||||
|     white-space: nowrap; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   &.pool { |  | ||||||
|     padding-left: 5px; |  | ||||||
|     padding-right: 20px; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   &.merkle { |  | ||||||
|     width: 100px; |  | ||||||
|     .pipe-segment { |  | ||||||
|       position: absolute; |  | ||||||
|       border-color: white; |  | ||||||
|       box-sizing: content-box; |  | ||||||
| 
 |  | ||||||
|       &.vertical { |  | ||||||
|         top: 0; |  | ||||||
|         right: 0; |  | ||||||
|         width: 50%; |  | ||||||
|         height: 100%; |  | ||||||
|         border-left: solid 4px; |  | ||||||
|       } |  | ||||||
|       &.horizontal { |  | ||||||
|         bottom: 0; |  | ||||||
|         left: 0; |  | ||||||
|         width: 100%; |  | ||||||
|         height: 50%; |  | ||||||
|         border-top: solid 4px; |  | ||||||
|       } |  | ||||||
|       &.branch-top { |  | ||||||
|         bottom: 0; |  | ||||||
|         right: 0; |  | ||||||
|         width: 100%; |  | ||||||
|         height: 50%; |  | ||||||
|         border-top: solid 4px; |  | ||||||
|         &::after { |  | ||||||
|           content: ""; |  | ||||||
|           position: absolute; |  | ||||||
|           box-sizing: content-box; |  | ||||||
|           top: -4px; |  | ||||||
|           right: 0px; |  | ||||||
|           bottom: 0; |  | ||||||
|           width: 50%; |  | ||||||
|           border-top: solid 4px; |  | ||||||
|           border-left: solid 4px; |  | ||||||
|           border-top-left-radius: 5px; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       &.branch-mid { |  | ||||||
|         bottom: 0; |  | ||||||
|         right: 0px; |  | ||||||
|         width: 50%; |  | ||||||
|         height: 100%; |  | ||||||
|         border-left: solid 4px; |  | ||||||
|         &::after { |  | ||||||
|           content: ""; |  | ||||||
|           position: absolute; |  | ||||||
|           box-sizing: content-box; |  | ||||||
|           top: -4px; |  | ||||||
|           left: -4px; |  | ||||||
|           width: 100%; |  | ||||||
|           height: 50%; |  | ||||||
|           border-bottom: solid 4px; |  | ||||||
|           border-left: solid 4px; |  | ||||||
|           border-bottom-left-radius: 5px; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       &.branch-end { |  | ||||||
|         top: -4px; |  | ||||||
|         right: 0; |  | ||||||
|         width: 50%; |  | ||||||
|         height: 50%; |  | ||||||
|         border-bottom-left-radius: 5px; |  | ||||||
|         border-bottom: solid 4px; |  | ||||||
|         border-left: solid 4px; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .cell-link { |  | ||||||
|     position: absolute; |  | ||||||
|     top: 0; |  | ||||||
|     left: 0; |  | ||||||
|     width: 100%; |  | ||||||
|     height: 100%; |  | ||||||
|     color: inherit; |  | ||||||
|     text-decoration: none; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @media (max-width: 800px) { |  | ||||||
|   .stratum-table { |  | ||||||
|     td { |  | ||||||
|       &.tag { |  | ||||||
|         display: none; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @media (max-width: 650px) { |  | ||||||
|   .stratum-table { |  | ||||||
|     td { |  | ||||||
|       &.reward { |  | ||||||
|         display: none; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .badge { |  | ||||||
|   position: relative; |  | ||||||
|   color: #FFF; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .pool-logo { |  | ||||||
|   width: 15px; |  | ||||||
|   height: 15px; |  | ||||||
|   position: relative; |  | ||||||
|   top: -1px; |  | ||||||
|   margin-right: 2px; |  | ||||||
| } |  | ||||||
| @ -1,230 +0,0 @@ | |||||||
| import { Component, OnInit, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef } from '@angular/core'; |  | ||||||
| import { StateService } from '../../../services/state.service'; |  | ||||||
| import { WebsocketService } from '../../../services/websocket.service'; |  | ||||||
| import { map, Observable } from 'rxjs'; |  | ||||||
| import { StratumJob } from '../../../interfaces/websocket.interface'; |  | ||||||
| import { MiningService } from '../../../services/mining.service'; |  | ||||||
| import { SinglePoolStats } from '../../../interfaces/node-api.interface'; |  | ||||||
| 
 |  | ||||||
| type MerkleCellType = ' ' | '┬' | '├' | '└' | '│' | '─' | 'leaf'; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| interface TaggedStratumJob extends StratumJob { |  | ||||||
|   tag: string; |  | ||||||
|   merkleBranchIds: string[]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface MerkleCell { |  | ||||||
|   hash: string; |  | ||||||
|   type: MerkleCellType; |  | ||||||
|   job?: TaggedStratumJob; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface MerkleTree { |  | ||||||
|   hash?: string; |  | ||||||
|   job: string; |  | ||||||
|   size: number; |  | ||||||
|   children?: MerkleTree[]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface PoolRow { |  | ||||||
|   job: TaggedStratumJob; |  | ||||||
|   merkleCells: MerkleCell[]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function parseTag(scriptSig: string): string { |  | ||||||
|   const hex = scriptSig.slice(8).replace(/6d6d.{64}/, ''); |  | ||||||
|   const bytes: number[] = []; |  | ||||||
|   for (let i = 0; i < hex.length; i += 2) { |  | ||||||
|     bytes.push(parseInt(hex.substr(i, 2), 16)); |  | ||||||
|   } |  | ||||||
|   // eslint-disable-next-line no-control-regex
 |  | ||||||
|   const ascii = new TextDecoder('utf8').decode(Uint8Array.from(bytes)).replace(/\uFFFD/g, '').replace(/\\0/g, '').replace(/[\x00-\x1F\x7F-\x9F]/g, ''); |  | ||||||
|   if (ascii.includes('/ViaBTC/')) { |  | ||||||
|     return '/ViaBTC/'; |  | ||||||
|   } else if (ascii.includes('SpiderPool/')) { |  | ||||||
|     return 'SpiderPool/'; |  | ||||||
|   } |  | ||||||
|   return (ascii.match(/\/.*\//)?.[0] || ascii).trim(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function getMerkleBranchIds(merkleBranches: string[], numBranches: number, poolId: number): string[] { |  | ||||||
|   let lastHash = ''; |  | ||||||
|   const ids: string[] = []; |  | ||||||
|   for (let i = 0; i < numBranches; i++) { |  | ||||||
|     if (merkleBranches[i]) { |  | ||||||
|       lastHash = merkleBranches[i]; |  | ||||||
|       ids.push(`${i}-${lastHash}`); |  | ||||||
|     } else { |  | ||||||
|       ids.push(`${i}-${lastHash}-${poolId}`); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return ids; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @Component({ |  | ||||||
|   selector: 'app-stratum-list', |  | ||||||
|   templateUrl: './stratum-list.component.html', |  | ||||||
|   styleUrls: ['./stratum-list.component.scss'], |  | ||||||
|   changeDetection: ChangeDetectionStrategy.OnPush, |  | ||||||
| }) |  | ||||||
| export class StratumList implements OnInit, OnDestroy { |  | ||||||
|   rows$: Observable<PoolRow[]>; |  | ||||||
|   pools: { [id: number]: SinglePoolStats } = {}; |  | ||||||
|   poolsReady: boolean = false; |  | ||||||
| 
 |  | ||||||
|   constructor( |  | ||||||
|     private stateService: StateService, |  | ||||||
|     private websocketService: WebsocketService, |  | ||||||
|     private miningService: MiningService, |  | ||||||
|     private cd: ChangeDetectorRef, |  | ||||||
|   ) {} |  | ||||||
| 
 |  | ||||||
|   ngOnInit(): void { |  | ||||||
|     this.websocketService.want(['stats', 'blocks', 'mempool-blocks']); |  | ||||||
|     this.miningService.getPools().subscribe(pools => { |  | ||||||
|       this.pools = {}; |  | ||||||
|       for (const pool of pools) { |  | ||||||
|         this.pools[pool.unique_id] = pool; |  | ||||||
|       } |  | ||||||
|       this.poolsReady = true; |  | ||||||
|       this.cd.markForCheck(); |  | ||||||
|     }); |  | ||||||
|     this.rows$ = this.stateService.stratumJobs$.pipe( |  | ||||||
|       map((jobs) => this.processJobs(jobs)), |  | ||||||
|     ); |  | ||||||
|     this.websocketService.startTrackStratum('all'); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   processJobs(rawJobs: Record<string, StratumJob>): PoolRow[] { |  | ||||||
|     const numBranches = Math.max(...Object.values(rawJobs).map(job => job.merkleBranches.length)); |  | ||||||
|     const jobs: Record<string, TaggedStratumJob> = {}; |  | ||||||
|     for (const [id, job] of Object.entries(rawJobs)) { |  | ||||||
|       jobs[id] = { ...job, tag: parseTag(job.scriptsig), merkleBranchIds: getMerkleBranchIds(job.merkleBranches, numBranches, job.pool) }; |  | ||||||
|     } |  | ||||||
|     if (Object.keys(jobs).length === 0) { |  | ||||||
|       return []; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     let trees: MerkleTree[] = Object.keys(jobs).map(job => ({ |  | ||||||
|       job, |  | ||||||
|       size: 1, |  | ||||||
|     })); |  | ||||||
| 
 |  | ||||||
|     // build tree from bottom up
 |  | ||||||
|     for (let col = numBranches - 1; col >= 0; col--) { |  | ||||||
|       const groups: Record<string, MerkleTree[]> = {}; |  | ||||||
|       for (const tree of trees) { |  | ||||||
|         const branchId = jobs[tree.job].merkleBranchIds[col]; |  | ||||||
|         if (!groups[branchId]) { |  | ||||||
|           groups[branchId] = []; |  | ||||||
|         } |  | ||||||
|         groups[branchId].push(tree); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       trees = Object.values(groups).map(group => ({ |  | ||||||
|         hash: jobs[group[0].job].merkleBranches[col], |  | ||||||
|         job: group[0].job, |  | ||||||
|         children: group, |  | ||||||
|         size: group.reduce((acc, tree) => acc + tree.size, 0), |  | ||||||
|       })); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // initialize grid of cells
 |  | ||||||
|     const rows: (MerkleCell | null)[][] = []; |  | ||||||
|     for (let i = 0; i < Object.keys(jobs).length; i++) { |  | ||||||
|       const row: (MerkleCell | null)[] = []; |  | ||||||
|       for (let j = 0; j <= numBranches; j++) { |  | ||||||
|         row.push(null); |  | ||||||
|       } |  | ||||||
|       rows.push(row); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // fill in the cells
 |  | ||||||
|     let colTrees = [trees.sort((a, b) => { |  | ||||||
|       if (a.size !== b.size) { |  | ||||||
|         return b.size - a.size; |  | ||||||
|       } |  | ||||||
|       return a.job.localeCompare(b.job); |  | ||||||
|     })]; |  | ||||||
|     for (let col = 0; col <= numBranches; col++) { |  | ||||||
|       let row = 0; |  | ||||||
|       const nextTrees: MerkleTree[][] = []; |  | ||||||
|       for (let g = 0; g < colTrees.length; g++) { |  | ||||||
|         for (let t = 0; t < colTrees[g].length; t++) { |  | ||||||
|           const tree = colTrees[g][t]; |  | ||||||
|           const isFirstTree = (t === 0); |  | ||||||
|           const isLastTree = (t === colTrees[g].length - 1); |  | ||||||
|           for (let i = 0; i < tree.size; i++) { |  | ||||||
|             const isFirstCell = (i === 0); |  | ||||||
|             const isLeaf = (col === numBranches); |  | ||||||
|             rows[row][col] = { |  | ||||||
|               hash: tree.hash, |  | ||||||
|               job: isLeaf ? jobs[tree.job] : undefined, |  | ||||||
|               type: 'leaf', |  | ||||||
|             }; |  | ||||||
|             if (col > 0) { |  | ||||||
|               rows[row][col - 1].type = getCellType(isFirstCell, isFirstTree, isLastTree); |  | ||||||
|             } |  | ||||||
|             row++; |  | ||||||
|           } |  | ||||||
|           if (tree.children) { |  | ||||||
|             nextTrees.push(tree.children.sort((a, b) => { |  | ||||||
|               if (a.size !== b.size) { |  | ||||||
|                 return b.size - a.size; |  | ||||||
|               } |  | ||||||
|               return a.job.localeCompare(b.job); |  | ||||||
|             })); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       colTrees = nextTrees; |  | ||||||
|     } |  | ||||||
|     return rows.map(row => ({ |  | ||||||
|       job: row[row.length - 1].job, |  | ||||||
|       merkleCells: row.slice(0, -1), |  | ||||||
|     })); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   pipeToClass(type: MerkleCellType): string { |  | ||||||
|     return { |  | ||||||
|       ' ': 'empty', |  | ||||||
|       '┬': 'branch-top', |  | ||||||
|       '├': 'branch-mid', |  | ||||||
|       '└': 'branch-end', |  | ||||||
|       '│': 'vertical', |  | ||||||
|       '─': 'horizontal', |  | ||||||
|       'leaf': 'leaf' |  | ||||||
|     }[type]; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   reverseHash(hash: string) { |  | ||||||
|     return hash.match(/../g).reverse().join(''); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   ngOnDestroy(): void { |  | ||||||
|     this.websocketService.stopTrackStratum(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function getCellType(isFirstCell, isFirstTree, isLastTree): MerkleCellType { |  | ||||||
|   if (isFirstCell) { |  | ||||||
|     if (isFirstTree) { |  | ||||||
|       if (isLastTree) { |  | ||||||
|         return '─'; |  | ||||||
|       } else { |  | ||||||
|         return '┬'; |  | ||||||
|       } |  | ||||||
|     } else if (isLastTree) { |  | ||||||
|       return '└'; |  | ||||||
|     } else { |  | ||||||
|       return '├'; |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     if (isLastTree) { |  | ||||||
|       return ' '; |  | ||||||
|     } else { |  | ||||||
|       return '│'; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -1,47 +1,4 @@ | |||||||
| <ng-container [ngSwitch]="name"> | <ng-container [ngSwitch]="name"> | ||||||
|   <ng-container *ngSwitchCase="'VISA'"> |  | ||||||
|     <svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg"> |  | ||||||
|       <!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--> |  | ||||||
|       <path d="M470.1 231.3s7.6 37.2 9.3 45H446c3.3-8.9 16-43.5 16-43.5-.2 .3 3.3-9.1 5.3-14.9l2.8 13.4zM576 80v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h480c26.5 0 48 21.5 48 48zM152.5 331.2L215.7 176h-42.5l-39.3 106-4.3-21.5-14-71.4c-2.3-9.9-9.4-12.7-18.2-13.1H32.7l-.7 3.1c15.8 4 29.9 9.8 42.2 17.1l35.8 135h42.5zm94.4 .2L272.1 176h-40.2l-25.1 155.4h40.1zm139.9-50.8c.2-17.7-10.6-31.2-33.7-42.3-14.1-7.1-22.7-11.9-22.7-19.2 .2-6.6 7.3-13.4 23.1-13.4 13.1-.3 22.7 2.8 29.9 5.9l3.6 1.7 5.5-33.6c-7.9-3.1-20.5-6.6-36-6.6-39.7 0-67.6 21.2-67.8 51.4-.3 22.3 20 34.7 35.2 42.2 15.5 7.6 20.8 12.6 20.8 19.3-.2 10.4-12.6 15.2-24.1 15.2-16 0-24.6-2.5-37.7-8.3l-5.3-2.5-5.6 34.9c9.4 4.3 26.8 8.1 44.8 8.3 42.2 .1 69.7-20.8 70-53zM528 331.4L495.6 176h-31.1c-9.6 0-16.9 2.8-21 12.9l-59.7 142.5H426s6.9-19.2 8.4-23.3H486c1.2 5.5 4.8 23.3 4.8 23.3H528z"/> |  | ||||||
|     </svg> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <ng-container *ngSwitchCase="'MASTERCARD'"> |  | ||||||
|     <svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg"> |  | ||||||
|       <!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M482.9 410.3c0 6.8-4.6 11.7-11.2 11.7-6.8 0-11.2-5.2-11.2-11.7 0-6.5 4.4-11.7 11.2-11.7 6.6 0 11.2 5.2 11.2 11.7zm-310.8-11.7c-7.1 0-11.2 5.2-11.2 11.7 0 6.5 4.1 11.7 11.2 11.7 6.5 0 10.9-4.9 10.9-11.7-.1-6.5-4.4-11.7-10.9-11.7zm117.5-.3c-5.4 0-8.7 3.5-9.5 8.7h19.1c-.9-5.7-4.4-8.7-9.6-8.7zm107.8 .3c-6.8 0-10.9 5.2-10.9 11.7 0 6.5 4.1 11.7 10.9 11.7 6.8 0 11.2-4.9 11.2-11.7 0-6.5-4.4-11.7-11.2-11.7zm105.9 26.1c0 .3 .3 .5 .3 1.1 0 .3-.3 .5-.3 1.1-.3 .3-.3 .5-.5 .8-.3 .3-.5 .5-1.1 .5-.3 .3-.5 .3-1.1 .3-.3 0-.5 0-1.1-.3-.3 0-.5-.3-.8-.5-.3-.3-.5-.5-.5-.8-.3-.5-.3-.8-.3-1.1 0-.5 0-.8 .3-1.1 0-.5 .3-.8 .5-1.1 .3-.3 .5-.3 .8-.5 .5-.3 .8-.3 1.1-.3 .5 0 .8 0 1.1 .3 .5 .3 .8 .3 1.1 .5s.2 .6 .5 1.1zm-2.2 1.4c.5 0 .5-.3 .8-.3 .3-.3 .3-.5 .3-.8 0-.3 0-.5-.3-.8-.3 0-.5-.3-1.1-.3h-1.6v3.5h.8V426h.3l1.1 1.4h.8l-1.1-1.3zM576 81v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V81c0-26.5 21.5-48 48-48h480c26.5 0 48 21.5 48 48zM64 220.6c0 76.5 62.1 138.5 138.5 138.5 27.2 0 53.9-8.2 76.5-23.1-72.9-59.3-72.4-171.2 0-230.5-22.6-15-49.3-23.1-76.5-23.1-76.4-.1-138.5 62-138.5 138.2zm224 108.8c70.5-55 70.2-162.2 0-217.5-70.2 55.3-70.5 162.6 0 217.5zm-142.3 76.3c0-8.7-5.7-14.4-14.7-14.7-4.6 0-9.5 1.4-12.8 6.5-2.4-4.1-6.5-6.5-12.2-6.5-3.8 0-7.6 1.4-10.6 5.4V392h-8.2v36.7h8.2c0-18.9-2.5-30.2 9-30.2 10.2 0 8.2 10.2 8.2 30.2h7.9c0-18.3-2.5-30.2 9-30.2 10.2 0 8.2 10 8.2 30.2h8.2v-23zm44.9-13.7h-7.9v4.4c-2.7-3.3-6.5-5.4-11.7-5.4-10.3 0-18.2 8.2-18.2 19.3 0 11.2 7.9 19.3 18.2 19.3 5.2 0 9-1.9 11.7-5.4v4.6h7.9V392zm40.5 25.6c0-15-22.9-8.2-22.9-15.2 0-5.7 11.9-4.8 18.5-1.1l3.3-6.5c-9.4-6.1-30.2-6-30.2 8.2 0 14.3 22.9 8.3 22.9 15 0 6.3-13.5 5.8-20.7 .8l-3.5 6.3c11.2 7.6 32.6 6 32.6-7.5zm35.4 9.3l-2.2-6.8c-3.8 2.1-12.2 4.4-12.2-4.1v-16.6h13.1V392h-13.1v-11.2h-8.2V392h-7.6v7.3h7.6V416c0 17.6 17.3 14.4 22.6 10.9zm13.3-13.4h27.5c0-16.2-7.4-22.6-17.4-22.6-10.6 0-18.2 7.9-18.2 19.3 0 20.5 22.6 23.9 33.8 14.2l-3.8-6c-7.8 6.4-19.6 5.8-21.9-4.9zm59.1-21.5c-4.6-2-11.6-1.8-15.2 4.4V392h-8.2v36.7h8.2V408c0-11.6 9.5-10.1 12.8-8.4l2.4-7.6zm10.6 18.3c0-11.4 11.6-15.1 20.7-8.4l3.8-6.5c-11.6-9.1-32.7-4.1-32.7 15 0 19.8 22.4 23.8 32.7 15l-3.8-6.5c-9.2 6.5-20.7 2.6-20.7-8.6zm66.7-18.3H408v4.4c-8.3-11-29.9-4.8-29.9 13.9 0 19.2 22.4 24.7 29.9 13.9v4.6h8.2V392zm33.7 0c-2.4-1.2-11-2.9-15.2 4.4V392h-7.9v36.7h7.9V408c0-11 9-10.3 12.8-8.4l2.4-7.6zm40.3-14.9h-7.9v19.3c-8.2-10.9-29.9-5.1-29.9 13.9 0 19.4 22.5 24.6 29.9 13.9v4.6h7.9v-51.7zm7.6-75.1v4.6h.8V302h1.9v-.8h-4.6v.8h1.9zm6.6 123.8c0-.5 0-1.1-.3-1.6-.3-.3-.5-.8-.8-1.1-.3-.3-.8-.5-1.1-.8-.5 0-1.1-.3-1.6-.3-.3 0-.8 .3-1.4 .3-.5 .3-.8 .5-1.1 .8-.5 .3-.8 .8-.8 1.1-.3 .5-.3 1.1-.3 1.6 0 .3 0 .8 .3 1.4 0 .3 .3 .8 .8 1.1 .3 .3 .5 .5 1.1 .8 .5 .3 1.1 .3 1.4 .3 .5 0 1.1 0 1.6-.3 .3-.3 .8-.5 1.1-.8 .3-.3 .5-.8 .8-1.1 .3-.6 .3-1.1 .3-1.4zm3.2-124.7h-1.4l-1.6 3.5-1.6-3.5h-1.4v5.4h.8v-4.1l1.6 3.5h1.1l1.4-3.5v4.1h1.1v-5.4zm4.4-80.5c0-76.2-62.1-138.3-138.5-138.3-27.2 0-53.9 8.2-76.5 23.1 72.1 59.3 73.2 171.5 0 230.5 22.6 15 49.5 23.1 76.5 23.1 76.4 .1 138.5-61.9 138.5-138.4z"/> |  | ||||||
|     </svg> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <ng-container *ngSwitchCase="'JCB'"> |  | ||||||
|     <svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg"> |  | ||||||
|       <!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M431.5 244.3V212c41.2 0 38.5 .2 38.5 .2 7.3 1.3 13.3 7.3 13.3 16 0 8.8-6 14.5-13.3 15.8-1.2 .4-3.3 .3-38.5 .3zm42.8 20.2c-2.8-.7-3.3-.5-42.8-.5v35c39.6 0 40 .2 42.8-.5 7.5-1.5 13.5-8 13.5-17 0-8.7-6-15.5-13.5-17zM576 80v352c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V80c0-26.5 21.5-48 48-48h480c26.5 0 48 21.5 48 48zM182 192.3h-57c0 67.1 10.7 109.7-35.8 109.7-19.5 0-38.8-5.7-57.2-14.8v28c30 8.3 68 8.3 68 8.3 97.9 0 82-47.7 82-131.2zm178.5 4.5c-63.4-16-165-14.9-165 59.3 0 77.1 108.2 73.6 165 59.2V287C312.9 311.7 253 309 253 256s59.8-55.6 107.5-31.2v-28zM544 286.5c0-18.5-16.5-30.5-38-32v-.8c19.5-2.7 30.3-15.5 30.3-30.2 0-19-15.7-30-37-31 0 0 6.3-.3-120.3-.3v127.5h122.7c24.3 .1 42.3-12.9 42.3-33.2z"/> |  | ||||||
|     </svg> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <ng-container *ngSwitchCase="'DISCOVER'"> |  | ||||||
|     <svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg"> |  | ||||||
|       <!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M520.4 196.1c0-7.9-5.5-12.1-15.6-12.1h-4.9v24.9h4.7c10.3 0 15.8-4.4 15.8-12.8zM528 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h480c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-44.1 138.9c22.6 0 52.9-4.1 52.9 24.4 0 12.6-6.6 20.7-18.7 23.2l25.8 34.4h-19.6l-22.2-32.8h-2.2v32.8h-16zm-55.9 .1h45.3v14H444v18.2h28.3V217H444v22.2h29.3V253H428zm-68.7 0l21.9 55.2 22.2-55.2h17.5l-35.5 84.2h-8.6l-35-84.2zm-55.9-3c24.7 0 44.6 20 44.6 44.6 0 24.7-20 44.6-44.6 44.6-24.7 0-44.6-20-44.6-44.6 0-24.7 20-44.6 44.6-44.6zm-49.3 6.1v19c-20.1-20.1-46.8-4.7-46.8 19 0 25 27.5 38.5 46.8 19.2v19c-29.7 14.3-63.3-5.7-63.3-38.2 0-31.2 33.1-53 63.3-38zm-97.2 66.3c11.4 0 22.4-15.3-3.3-24.4-15-5.5-20.2-11.4-20.2-22.7 0-23.2 30.6-31.4 49.7-14.3l-8.4 10.8c-10.4-11.6-24.9-6.2-24.9 2.5 0 4.4 2.7 6.9 12.3 10.3 18.2 6.6 23.6 12.5 23.6 25.6 0 29.5-38.8 37.4-56.6 11.3l10.3-9.9c3.7 7.1 9.9 10.8 17.5 10.8zM55.4 253H32v-82h23.4c26.1 0 44.1 17 44.1 41.1 0 18.5-13.2 40.9-44.1 40.9zm67.5 0h-16v-82h16zM544 433c0 8.2-6.8 15-15 15H128c189.6-35.6 382.7-139.2 416-160zM74.1 191.6c-5.2-4.9-11.6-6.6-21.9-6.6H48v54.2h4.2c10.3 0 17-2 21.9-6.4 5.7-5.2 8.9-12.8 8.9-20.7s-3.2-15.5-8.9-20.5z"/> |  | ||||||
|     </svg> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <ng-container *ngSwitchCase="'DISCOVER_DINERS'"> |  | ||||||
|     <svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg"> |  | ||||||
|       <!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M239.7 79.9c-96.9 0-175.8 78.6-175.8 175.8 0 96.9 78.9 175.8 175.8 175.8 97.2 0 175.8-78.9 175.8-175.8 0-97.2-78.6-175.8-175.8-175.8zm-39.9 279.6c-41.7-15.9-71.4-56.4-71.4-103.8s29.7-87.9 71.4-104.1v207.9zm79.8 .3V151.6c41.7 16.2 71.4 56.7 71.4 104.1s-29.7 87.9-71.4 104.1zM528 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h480c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM329.7 448h-90.3c-106.2 0-193.8-85.5-193.8-190.2C45.6 143.2 133.2 64 239.4 64h90.3c105 0 200.7 79.2 200.7 193.8 0 104.7-95.7 190.2-200.7 190.2z"/> |  | ||||||
|     </svg> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <ng-container *ngSwitchCase="'AMERICAN_EXPRESS'"> |  | ||||||
|     <svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg"> |  | ||||||
|       <!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M0 432c0 26.5 21.5 48 48 48H528c26.5 0 48-21.5 48-48v-1.1H514.3l-31.9-35.1-31.9 35.1H246.8V267.1H181L262.7 82.4h78.6l28.1 63.2V82.4h97.2L483.5 130l17-47.6H576V80c0-26.5-21.5-48-48-48H48C21.5 32 0 53.5 0 80V432zm440.4-21.7L482.6 364l42 46.3H576l-68-72.1 68-72.1H525.4l-42 46.7-41.5-46.7H390.5L458 338.6l-67.4 71.6V377.1h-83V354.9h80.9V322.6H307.6V300.2h83V267.1h-122V410.3H440.4zm96.3-72L576 380.2V296.9l-39.3 41.4zm-36.3-92l36.9-100.6V246.3H576V103H515.8l-32.2 89.3L451.7 103H390.5V246.1L327.3 103H276.1L213.7 246.3h43l11.9-28.7h65.9l12 28.7h82.7V146L466 246.3h34.4zM282 185.4l19.5-46.9 19.4 46.9H282z"/> |  | ||||||
|     </svg> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <ng-container *ngSwitchCase="'OTHER_BRAND'"> |  | ||||||
|     <svg xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" viewBox="0 0 576 512" fill="white" xmlns="http://www.w3.org/2000/svg"> |  | ||||||
|       <!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M512 80c8.8 0 16 7.2 16 16l0 32L48 128l0-32c0-8.8 7.2-16 16-16l448 0zm16 144l0 192c0 8.8-7.2 16-16 16L64 432c-8.8 0-16-7.2-16-16l0-192 480 0zM64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l448 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32zm56 304c-13.3 0-24 10.7-24 24s10.7 24 24 24l48 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-48 0zm128 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l112 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-112 0z"/> |  | ||||||
|     </svg> |  | ||||||
|   </ng-container> |  | ||||||
| 
 |  | ||||||
|   <ng-container *ngSwitchCase="'officialMempoolSpace'"> |   <ng-container *ngSwitchCase="'officialMempoolSpace'"> | ||||||
|     <svg [class]="class" [style]="style" [attr.width]="width" [attr.height]="height" [attr.viewBox]="viewBox" fill="none" xmlns="http://www.w3.org/2000/svg"> |     <svg [class]="class" [style]="style" [attr.width]="width" [attr.height]="height" [attr.viewBox]="viewBox" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|       <path d="M163.658 113.263C161.089 113.263 158.992 111.146 158.992 108.535C158.992 105.966 161.048 103.951 163.658 103.951C166.269 103.951 168.325 105.966 168.325 108.535C168.325 111.125 166.228 113.263 163.658 113.263Z" fill="#9857FF"/> |       <path d="M163.658 113.263C161.089 113.263 158.992 111.146 158.992 108.535C158.992 105.966 161.048 103.951 163.658 103.951C166.269 103.951 168.325 105.966 168.325 108.535C168.325 111.125 166.228 113.263 163.658 113.263Z" fill="#9857FF"/> | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ | |||||||
|           </a> |           </a> | ||||||
|         } @else if (enterpriseInfo?.img || enterpriseInfo?.imageMd5) { |         } @else if (enterpriseInfo?.img || enterpriseInfo?.imageMd5) { | ||||||
|           <a [routerLink]="['/' | relativeUrl]"> |           <a [routerLink]="['/' | relativeUrl]"> | ||||||
|             <img [src]="enterpriseInfo.img || '/api/v1/services/enterprise/images/' + enterpriseInfo.name + '/logo'" class="subdomain_logo" [class]="{'rounded': enterpriseInfo.rounded_corner}"> |             <img [src]="enterpriseInfo.img || '/api/v1/services/enterprise/images/' + enterpriseInfo.name + '/logo?imageMd5=' + enterpriseInfo.imageMd5" class="subdomain_logo" [class]="{'rounded': enterpriseInfo.rounded_corner}"> | ||||||
|           </a> |           </a> | ||||||
|           <div class="vertical-line"></div> |           <div class="vertical-line"></div> | ||||||
|         } |         } | ||||||
| @ -124,6 +124,7 @@ | |||||||
|           <ng-container *ngIf="(ETA$ | async) as eta;"> |           <ng-container *ngIf="(ETA$ | async) as eta;"> | ||||||
|             <app-accelerate-checkout |             <app-accelerate-checkout | ||||||
|               *ngIf="(da$ | async) as da;" |               *ngIf="(da$ | async) as da;" | ||||||
|  |               [cashappEnabled]="cashappEligible" | ||||||
|               [advancedEnabled]="false" |               [advancedEnabled]="false" | ||||||
|               [forceMobile]="true" |               [forceMobile]="true" | ||||||
|               [tx]="tx" |               [tx]="tx" | ||||||
|  | |||||||
| @ -756,6 +756,10 @@ export class TrackerComponent implements OnInit, OnDestroy { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   get cashappEligible(): boolean { | ||||||
|  |     return this.mempoolPosition?.block > 0 && this.tx.weight < 4000; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   get showAccelerationSummary(): boolean { |   get showAccelerationSummary(): boolean { | ||||||
|     return ( |     return ( | ||||||
|       this.tx |       this.tx | ||||||
|  | |||||||
| @ -24,7 +24,6 @@ | |||||||
|           [height]="tx?.status?.block_height" |           [height]="tx?.status?.block_height" | ||||||
|           [replaced]="replaced" |           [replaced]="replaced" | ||||||
|           [removed]="this.rbfInfo?.mined && !this.tx?.status?.confirmed" |           [removed]="this.rbfInfo?.mined && !this.tx?.status?.confirmed" | ||||||
|           [cached]="isCached" |  | ||||||
|         ></app-confirmations> |         ></app-confirmations> | ||||||
|       </div> |       </div> | ||||||
|     </ng-container> |     </ng-container> | ||||||
| @ -139,6 +138,7 @@ | |||||||
| 
 | 
 | ||||||
|       <app-accelerate-checkout |       <app-accelerate-checkout | ||||||
|         *ngIf="(da$ | async) as da;" |         *ngIf="(da$ | async) as da;" | ||||||
|  |         [cashappEnabled]="cashappEligible" | ||||||
|         [advancedEnabled]="true" |         [advancedEnabled]="true" | ||||||
|         [tx]="tx" |         [tx]="tx" | ||||||
|         [accelerating]="isAcceleration" |         [accelerating]="isAcceleration" | ||||||
|  | |||||||
| @ -156,6 +156,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|   showAccelerationDetails = false; |   showAccelerationDetails = false; | ||||||
|   hasAccelerationDetails = false; |   hasAccelerationDetails = false; | ||||||
|   scrollIntoAccelPreview = false; |   scrollIntoAccelPreview = false; | ||||||
|  |   cashappEligible = false; | ||||||
|   auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true; |   auditEnabled: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true; | ||||||
|   isMempoolSpaceBuild = this.stateService.isMempoolSpaceBuild; |   isMempoolSpaceBuild = this.stateService.isMempoolSpaceBuild; | ||||||
| 
 | 
 | ||||||
| @ -527,6 +528,9 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|                   this.miningStats = stats; |                   this.miningStats = stats; | ||||||
|                 }); |                 }); | ||||||
|               } |               } | ||||||
|  |               if (txPosition.position?.block > 0 && this.tx.weight < 4000) { | ||||||
|  |                 this.cashappEligible = true; | ||||||
|  |               } | ||||||
|               if (!this.gotInitialPosition && txPosition.position?.block === 0 && txPosition.position?.vsize < 750_000) { |               if (!this.gotInitialPosition && txPosition.position?.block === 0 && txPosition.position?.vsize < 750_000) { | ||||||
|                 this.accelerationFlowCompleted = true; |                 this.accelerationFlowCompleted = true; | ||||||
|               } |               } | ||||||
| @ -1032,6 +1036,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { | |||||||
|     this.showAccelerationDetails = false; |     this.showAccelerationDetails = false; | ||||||
|     this.accelerationFlowCompleted = false; |     this.accelerationFlowCompleted = false; | ||||||
|     this.accelerationInfo = null; |     this.accelerationInfo = null; | ||||||
|  |     this.cashappEligible = false; | ||||||
|     this.txInBlockIndex = null; |     this.txInBlockIndex = null; | ||||||
|     this.mempoolPosition = null; |     this.mempoolPosition = null; | ||||||
|     this.pool = null; |     this.pool = null; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| <a href="#" (click)="twitterLogin()" | <a href="#" (click)="twitterLogin()" | ||||||
|   [class]="(disabled ? 'disabled': '') + (customClass ? customClass : 'w-100 btn mt-1 d-flex justify-content-center align-items-center')" |   [class]="(disabled ? 'disabled': '') + (customClass ? customClass : 'w-100 btn mt-1 d-flex justify-content-center align-items-center')" | ||||||
|   style="background-color: rgb(31, 35, 40)" [style]="width ? 'width: ' + width : ''"> |   style="background-color: #1DA1F2" [style]="width ? 'width: ' + width : ''"> | ||||||
|  |   <img src="./resources/twitter.svg" height="25" style="padding: 2px" [alt]="buttonString + ' with Twitter'" /> | ||||||
|   <span class="ml-2 text-light align-middle">{{ buttonString }}</span> |   <span class="ml-2 text-light align-middle">{{ buttonString }}</span> | ||||||
|   <img src="./resources/x.svg" height="25" style="padding: 2px; padding-left: 5px" [alt]="buttonString + ' with Twitter'" /> |  | ||||||
| </a> | </a> | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| <div class="container-xl" [class.liquid-address]="network === 'liquid' || network === 'liquidtestnet'"> | <div class="container-xl" [class.liquid-address]="network === 'liquid' || network === 'liquidtestnet'"> | ||||||
|   <div class="title-address"> |   <div class="title-address"> | ||||||
|     <h1>{{ walletName }}</h1> |     <h1 i18n="shared.wallet">Wallet</h1> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <div class="clearfix"></div> |   <div class="clearfix"></div> | ||||||
| @ -74,36 +74,6 @@ | |||||||
|     </ng-container> |     </ng-container> | ||||||
|   </ng-container> |   </ng-container> | ||||||
| 
 | 
 | ||||||
|   <br> |  | ||||||
| 
 |  | ||||||
|   <div class="title-tx"> |  | ||||||
|     <h2 class="text-left" i18n="address.transactions">Transactions</h2> |  | ||||||
|   </div> |  | ||||||
| 
 |  | ||||||
|   <app-transactions-list [transactions]="transactions" [showConfirmations]="true" [addresses]="addressStrings" (loadMore)="loadMore()"></app-transactions-list> |  | ||||||
| 
 |  | ||||||
|   <div class="text-center"> |  | ||||||
|     <ng-template [ngIf]="isLoadingTransactions"> |  | ||||||
|       <div class="header-bg box"> |  | ||||||
|         <div class="row" style="height: 107px;"> |  | ||||||
|           <div class="col-sm"> |  | ||||||
|             <span class="skeleton-loader"></span> |  | ||||||
|           </div> |  | ||||||
|           <div class="col-sm"> |  | ||||||
|             <span class="skeleton-loader"></span> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
| 
 |  | ||||||
|     </ng-template> |  | ||||||
| 
 |  | ||||||
|     <ng-template [ngIf]="retryLoadMore"> |  | ||||||
|       <br> |  | ||||||
|       <button type="button" class="btn btn-outline-info btn-sm" (click)="loadMore()"><fa-icon [icon]="['fas', 'redo-alt']" [fixedWidth]="true"></fa-icon></button> |  | ||||||
|     </ng-template> |  | ||||||
|   </div> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|   <ng-template #loadingTemplate> |   <ng-template #loadingTemplate> | ||||||
| 
 | 
 | ||||||
|     <div class="box" *ngIf="!error; else errorTemplate"> |     <div class="box" *ngIf="!error; else errorTemplate"> | ||||||
|  | |||||||
| @ -9,8 +9,6 @@ import { of, Observable, Subscription } from 'rxjs'; | |||||||
| import { SeoService } from '@app/services/seo.service'; | import { SeoService } from '@app/services/seo.service'; | ||||||
| import { seoDescriptionNetwork } from '@app/shared/common.utils'; | import { seoDescriptionNetwork } from '@app/shared/common.utils'; | ||||||
| import { WalletAddress } from '@interfaces/node-api.interface'; | import { WalletAddress } from '@interfaces/node-api.interface'; | ||||||
| import { ElectrsApiService } from '@app/services/electrs-api.service'; |  | ||||||
| import { AudioService } from '@app/services/audio.service'; |  | ||||||
| 
 | 
 | ||||||
| class WalletStats implements ChainStats { | class WalletStats implements ChainStats { | ||||||
|   addresses: string[]; |   addresses: string[]; | ||||||
| @ -26,7 +24,6 @@ class WalletStats implements ChainStats { | |||||||
|         acc.funded_txo_sum += stat.funded_txo_sum; |         acc.funded_txo_sum += stat.funded_txo_sum; | ||||||
|         acc.spent_txo_count += stat.spent_txo_count; |         acc.spent_txo_count += stat.spent_txo_count; | ||||||
|         acc.spent_txo_sum += stat.spent_txo_sum; |         acc.spent_txo_sum += stat.spent_txo_sum; | ||||||
|         acc.tx_count += stat.tx_count; |  | ||||||
|         return acc; |         return acc; | ||||||
|       }, { |       }, { | ||||||
|         funded_txo_count: 0, |         funded_txo_count: 0, | ||||||
| @ -112,17 +109,12 @@ export class WalletComponent implements OnInit, OnDestroy { | |||||||
|   addressStrings: string[] = []; |   addressStrings: string[] = []; | ||||||
|   walletName: string; |   walletName: string; | ||||||
|   isLoadingWallet = true; |   isLoadingWallet = true; | ||||||
|   isLoadingTransactions = true; |  | ||||||
|   transactions: Transaction[]; |  | ||||||
|   totalTransactionCount: number; |  | ||||||
|   retryLoadMore = false; |  | ||||||
|   wallet$: Observable<Record<string, WalletAddress>>; |   wallet$: Observable<Record<string, WalletAddress>>; | ||||||
|   walletAddresses$: Observable<Record<string, Address>>; |   walletAddresses$: Observable<Record<string, Address>>; | ||||||
|   walletSummary$: Observable<AddressTxSummary[]>; |   walletSummary$: Observable<AddressTxSummary[]>; | ||||||
|   walletStats$: Observable<WalletStats>; |   walletStats$: Observable<WalletStats>; | ||||||
|   error: any; |   error: any; | ||||||
|   walletSubscription: Subscription; |   walletSubscription: Subscription; | ||||||
|   transactionSubscription: Subscription; |  | ||||||
| 
 | 
 | ||||||
|   collapseAddresses: boolean = true; |   collapseAddresses: boolean = true; | ||||||
| 
 | 
 | ||||||
| @ -137,8 +129,6 @@ export class WalletComponent implements OnInit, OnDestroy { | |||||||
|     private websocketService: WebsocketService, |     private websocketService: WebsocketService, | ||||||
|     private stateService: StateService, |     private stateService: StateService, | ||||||
|     private apiService: ApiService, |     private apiService: ApiService, | ||||||
|     private electrsApiService: ElectrsApiService, |  | ||||||
|     private audioService: AudioService, |  | ||||||
|     private seoService: SeoService, |     private seoService: SeoService, | ||||||
|   ) { } |   ) { } | ||||||
| 
 | 
 | ||||||
| @ -182,21 +172,6 @@ export class WalletComponent implements OnInit, OnDestroy { | |||||||
|       }), |       }), | ||||||
|       switchMap(initial => this.stateService.walletTransactions$.pipe( |       switchMap(initial => this.stateService.walletTransactions$.pipe( | ||||||
|         startWith(null), |         startWith(null), | ||||||
|         tap((transactions) => { |  | ||||||
|           if (!transactions?.length) { |  | ||||||
|             return; |  | ||||||
|           } |  | ||||||
|           for (const transaction of transactions) { |  | ||||||
|             const tx = this.transactions.find((t) => t.txid === transaction.txid); |  | ||||||
|             if (tx) { |  | ||||||
|               tx.status = transaction.status; |  | ||||||
|             } else { |  | ||||||
|               this.transactions.unshift(transaction); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           this.transactions = this.transactions.slice(); |  | ||||||
|           this.audioService.playSound('magic'); |  | ||||||
|         }), |  | ||||||
|         scan((wallet, walletTransactions) => { |         scan((wallet, walletTransactions) => { | ||||||
|           for (const tx of (walletTransactions || [])) { |           for (const tx of (walletTransactions || [])) { | ||||||
|             const funded: Record<string, number> = {}; |             const funded: Record<string, number> = {}; | ||||||
| @ -292,57 +267,8 @@ export class WalletComponent implements OnInit, OnDestroy { | |||||||
|             return stats; |             return stats; | ||||||
|           }, walletStats), |           }, walletStats), | ||||||
|         ); |         ); | ||||||
|       }) |       }), | ||||||
|     ); |     ); | ||||||
| 
 |  | ||||||
|     this.transactionSubscription = this.wallet$.pipe( |  | ||||||
|       switchMap(wallet => { |  | ||||||
|         const addresses = Object.keys(wallet).map(addr => this.normalizeAddress(addr)); |  | ||||||
|         return this.electrsApiService.getAddressesTransactions$(addresses); |  | ||||||
|       }), |  | ||||||
|       map(transactions => { |  | ||||||
|         // only confirmed transactions supported for now
 |  | ||||||
|         return transactions.filter(tx => tx.status.confirmed).sort((a, b) => b.status.block_height - a.status.block_height); |  | ||||||
|       }), |  | ||||||
|       catchError((error) => { |  | ||||||
|         console.log(error); |  | ||||||
|         this.error = error; |  | ||||||
|         this.seoService.logSoft404(); |  | ||||||
|         this.isLoadingWallet = false; |  | ||||||
|         return of([]); |  | ||||||
|       }) |  | ||||||
|     ).subscribe((transactions: Transaction[] | null) => { |  | ||||||
|       if (!transactions) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       this.transactions = transactions; |  | ||||||
|       this.isLoadingTransactions = false; |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   loadMore(): void { |  | ||||||
|     if (this.isLoadingTransactions || this.fullyLoaded) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     this.isLoadingTransactions = true; |  | ||||||
|     this.retryLoadMore = false; |  | ||||||
|     this.electrsApiService.getAddressesTransactions$(this.addressStrings, this.transactions[this.transactions.length - 1].txid) |  | ||||||
|       .subscribe((transactions: Transaction[]) => { |  | ||||||
|         if (transactions && transactions.length) { |  | ||||||
|           this.transactions = this.transactions.concat(transactions.sort((a, b) => b.status.block_height - a.status.block_height)); |  | ||||||
|         } else { |  | ||||||
|           this.fullyLoaded = true; |  | ||||||
|         } |  | ||||||
|         this.isLoadingTransactions = false; |  | ||||||
|       }, |  | ||||||
|       (error) => { |  | ||||||
|         this.isLoadingTransactions = false; |  | ||||||
|         this.retryLoadMore = true; |  | ||||||
|         // In the unlikely event of the txid wasn't found in the mempool anymore and we must reload the page.
 |  | ||||||
|         if (error.status === 422) { |  | ||||||
|           window.location.reload(); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   deduplicateWalletTransactions(walletTransactions: AddressTxSummary[]): AddressTxSummary[] { |   deduplicateWalletTransactions(walletTransactions: AddressTxSummary[]): AddressTxSummary[] { | ||||||
| @ -373,6 +299,5 @@ export class WalletComponent implements OnInit, OnDestroy { | |||||||
|   ngOnDestroy(): void { |   ngOnDestroy(): void { | ||||||
|     this.websocketService.stopTrackingWallet(); |     this.websocketService.stopTrackingWallet(); | ||||||
|     this.walletSubscription.unsubscribe(); |     this.walletSubscription.unsubscribe(); | ||||||
|     this.transactionSubscription.unsubscribe(); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { AddressTxSummary, Block, ChainStats } from "./electrs.interface"; | import { AddressTxSummary, Block, ChainStats, Transaction } from "./electrs.interface"; | ||||||
| 
 | 
 | ||||||
| export interface OptimizedMempoolStats { | export interface OptimizedMempoolStats { | ||||||
|   added: number; |   added: number; | ||||||
| @ -412,13 +412,13 @@ export interface Acceleration { | |||||||
|   feeDelta: number; |   feeDelta: number; | ||||||
|   blockHash: string; |   blockHash: string; | ||||||
|   blockHeight: number; |   blockHeight: number; | ||||||
|  | 
 | ||||||
|   acceleratedFeeRate?: number; |   acceleratedFeeRate?: number; | ||||||
|   boost?: number; |   boost?: number; | ||||||
|   bidBoost?: number; |   bidBoost?: number; | ||||||
|   boostCost?: number; |   boostCost?: number; | ||||||
|   boostRate?: number; |   boostRate?: number; | ||||||
|   minedByPoolUniqueId?: number; |   minedByPoolUniqueId?: number; | ||||||
|   canceled?: number; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface AccelerationHistoryParams { | export interface AccelerationHistoryParams { | ||||||
|  | |||||||
| @ -21,8 +21,6 @@ export interface WebsocketResponse { | |||||||
|   rbfInfo?: RbfTree; |   rbfInfo?: RbfTree; | ||||||
|   rbfLatest?: RbfTree[]; |   rbfLatest?: RbfTree[]; | ||||||
|   rbfLatestSummary?: ReplacementInfo[]; |   rbfLatestSummary?: ReplacementInfo[]; | ||||||
|   stratumJob?: StratumJob; |  | ||||||
|   stratumJobs?: Record<number, StratumJob>; |  | ||||||
|   utxoSpent?: object; |   utxoSpent?: object; | ||||||
|   transactions?: TransactionStripped[]; |   transactions?: TransactionStripped[]; | ||||||
|   loadingIndicators?: ILoadingIndicators; |   loadingIndicators?: ILoadingIndicators; | ||||||
| @ -39,7 +37,6 @@ export interface WebsocketResponse { | |||||||
|   'track-rbf-summary'?: boolean; |   'track-rbf-summary'?: boolean; | ||||||
|   'track-accelerations'?: boolean; |   'track-accelerations'?: boolean; | ||||||
|   'track-wallet'?: string; |   'track-wallet'?: string; | ||||||
|   'track-stratum'?: string | number; |  | ||||||
|   'watch-mempool'?: boolean; |   'watch-mempool'?: boolean; | ||||||
|   'refresh-blocks'?: boolean; |   'refresh-blocks'?: boolean; | ||||||
| } | } | ||||||
| @ -153,24 +150,3 @@ export interface HealthCheckHost { | |||||||
|     electrs?: string; |     electrs?: string; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export interface StratumJob { |  | ||||||
|   pool: number; |  | ||||||
|   height: number; |  | ||||||
|   coinbase: string; |  | ||||||
|   scriptsig: string; |  | ||||||
|   reward: number; |  | ||||||
|   jobId: string; |  | ||||||
|   extraNonce: string; |  | ||||||
|   extraNonce2Size: number; |  | ||||||
|   prevHash: string; |  | ||||||
|   coinbase1: string; |  | ||||||
|   coinbase2: string; |  | ||||||
|   merkleBranches: string[]; |  | ||||||
|   version: string; |  | ||||||
|   bits: string; |  | ||||||
|   time: string; |  | ||||||
|   timestamp: number; |  | ||||||
|   cleanJobs: boolean; |  | ||||||
|   received: number; |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -10,10 +10,9 @@ import { TestTransactionsComponent } from '@components/test-transactions/test-tr | |||||||
| import { CalculatorComponent } from '@components/calculator/calculator.component'; | import { CalculatorComponent } from '@components/calculator/calculator.component'; | ||||||
| import { BlocksList } from '@components/blocks-list/blocks-list.component'; | import { BlocksList } from '@components/blocks-list/blocks-list.component'; | ||||||
| import { RbfList } from '@components/rbf-list/rbf-list.component'; | import { RbfList } from '@components/rbf-list/rbf-list.component'; | ||||||
| import { StratumList } from '@components/stratum/stratum-list/stratum-list.component'; |  | ||||||
| import { ServerHealthComponent } from '@components/server-health/server-health.component'; | import { ServerHealthComponent } from '@components/server-health/server-health.component'; | ||||||
| import { ServerStatusComponent } from '@components/server-health/server-status.component'; | import { ServerStatusComponent } from '@components/server-health/server-status.component'; | ||||||
| import { FaucetComponent } from '@components/faucet/faucet.component'; | import { FaucetComponent } from '@components/faucet/faucet.component' | ||||||
| 
 | 
 | ||||||
| const browserWindow = window || {}; | const browserWindow = window || {}; | ||||||
| // @ts-ignore
 | // @ts-ignore
 | ||||||
| @ -57,16 +56,6 @@ const routes: Routes = [ | |||||||
|         path: 'rbf', |         path: 'rbf', | ||||||
|         component: RbfList, |         component: RbfList, | ||||||
|       }, |       }, | ||||||
|       ...(browserWindowEnv.STRATUM_ENABLED ? [{ |  | ||||||
|         path: 'stratum', |  | ||||||
|         component: StartComponent, |  | ||||||
|         children: [ |  | ||||||
|           { |  | ||||||
|             path: '', |  | ||||||
|             component: StratumList, |  | ||||||
|           } |  | ||||||
|         ] |  | ||||||
|       }] : []), |  | ||||||
|       { |       { | ||||||
|         path: 'terms-of-service', |         path: 'terms-of-service', | ||||||
|         loadChildren: () => import('@components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), |         loadChildren: () => import('@components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), | ||||||
|  | |||||||
| @ -58,7 +58,6 @@ export class AuthServiceMempool { | |||||||
|   setAuth(auth: any) { |   setAuth(auth: any) { | ||||||
|     if (!auth) { |     if (!auth) { | ||||||
|       localStorage.removeItem('auth'); |       localStorage.removeItem('auth'); | ||||||
|       localStorage.removeItem('authenticatorStatus'); |  | ||||||
|     } else { |     } else { | ||||||
|       localStorage.setItem('auth', JSON.stringify(auth)); |       localStorage.setItem('auth', JSON.stringify(auth)); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -147,11 +147,7 @@ export class ElectrsApiService { | |||||||
|     if (txid) { |     if (txid) { | ||||||
|       params = params.append('after_txid', txid); |       params = params.append('after_txid', txid); | ||||||
|     } |     } | ||||||
|     return this.httpClient.post<Transaction[]>( |     return this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + `/api/addresses/txs?addresses=${addresses.join(',')}`, { params }); | ||||||
|       this.apiBaseUrl + this.apiBasePath + '/api/addresses/txs', |  | ||||||
|       addresses, |  | ||||||
|       { params } |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getAddressSummary$(address: string,  txid?: string): Observable<AddressTxSummary[]> { |   getAddressSummary$(address: string,  txid?: string): Observable<AddressTxSummary[]> { | ||||||
| @ -167,7 +163,7 @@ export class ElectrsApiService { | |||||||
|     if (txid) { |     if (txid) { | ||||||
|       params = params.append('after_txid', txid); |       params = params.append('after_txid', txid); | ||||||
|     } |     } | ||||||
|     return this.httpClient.post<AddressTxSummary[]>(this.apiBaseUrl + this.apiBasePath + '/api/addresses/txs/summary', addresses, { params }); |     return this.httpClient.get<AddressTxSummary[]>(this.apiBaseUrl + this.apiBasePath + `/api/addresses/txs/summary?addresses=${addresses.join(',')}`, { params }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getScriptHashTransactions$(script: string,  txid?: string): Observable<Transaction[]> { |   getScriptHashTransactions$(script: string,  txid?: string): Observable<Transaction[]> { | ||||||
| @ -186,7 +182,7 @@ export class ElectrsApiService { | |||||||
|       params = params.append('after_txid', txid); |       params = params.append('after_txid', txid); | ||||||
|     } |     } | ||||||
|     return from(Promise.all(scripts.map(script => calcScriptHash$(script)))).pipe( |     return from(Promise.all(scripts.map(script => calcScriptHash$(script)))).pipe( | ||||||
|       switchMap(scriptHashes => this.httpClient.post<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/scripthashes/txs', scriptHashes, { params })), |       switchMap(scriptHashes => this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + `/api/scripthashes/txs?scripthashes=${scriptHashes.join(',')}`, { params })), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -216,7 +212,7 @@ export class ElectrsApiService { | |||||||
|       params = params.append('after_txid', txid); |       params = params.append('after_txid', txid); | ||||||
|     } |     } | ||||||
|     return from(Promise.all(scripts.map(script => calcScriptHash$(script)))).pipe( |     return from(Promise.all(scripts.map(script => calcScriptHash$(script)))).pipe( | ||||||
|       switchMap(scriptHashes => this.httpClient.post<AddressTxSummary[]>(this.apiBaseUrl + this.apiBasePath + '/api/scripthashes/txs/summary', scriptHashes, { params })), |       switchMap(scriptHashes => this.httpClient.get<AddressTxSummary[]>(this.apiBaseUrl + this.apiBasePath + `/api/scripthashes/txs/summary?scripthashes=${scriptHashes.join(',')}`, { params })), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -75,6 +75,7 @@ export class MiningService { | |||||||
|         return this.poolsData; |         return this.poolsData; | ||||||
|       }) |       }) | ||||||
|     ); |     ); | ||||||
|  |      | ||||||
|   } |   } | ||||||
|   /** |   /** | ||||||
|    * Set the hashrate power of ten we want to display |    * Set the hashrate power of ten we want to display | ||||||
|  | |||||||
| @ -146,10 +146,6 @@ export class ServicesApiServices { | |||||||
|     return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD, userChallenged: userChallenged }); |     return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD, userChallenged: userChallenged }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   accelerateWithCardOnFile$(txInput: string, token: string, verificationToken: string, referenceId: string, userApprovedUSD: number, userChallenged: boolean) { |  | ||||||
|     return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/cardOnFile`, { txInput: txInput, token: token, verificationToken: verificationToken, referenceId: referenceId, userApprovedUSD: userApprovedUSD, userChallenged: userChallenged }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   getAccelerations$(): Observable<Acceleration[]> { |   getAccelerations$(): Observable<Acceleration[]> { | ||||||
|     return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations`); |     return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations`); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; | import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; | ||||||
| import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs'; | import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs'; | ||||||
| import { Transaction } from '@interfaces/electrs.interface'; | import { AddressTxSummary, Transaction } from '@interfaces/electrs.interface'; | ||||||
| import { AccelerationDelta, HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockUpdate, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, StratumJob, isMempoolState } from '@interfaces/websocket.interface'; | import { AccelerationDelta, HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockUpdate, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, isMempoolState } from '@interfaces/websocket.interface'; | ||||||
| import { Acceleration, AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '@interfaces/node-api.interface'; | import { Acceleration, AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '@interfaces/node-api.interface'; | ||||||
| import { Router, NavigationStart } from '@angular/router'; | import { Router, NavigationStart } from '@angular/router'; | ||||||
| import { isPlatformBrowser } from '@angular/common'; | import { isPlatformBrowser } from '@angular/common'; | ||||||
| @ -81,7 +81,6 @@ export interface Env { | |||||||
|   ADDITIONAL_CURRENCIES: boolean; |   ADDITIONAL_CURRENCIES: boolean; | ||||||
|   GIT_COMMIT_HASH_MEMPOOL_SPACE?: string; |   GIT_COMMIT_HASH_MEMPOOL_SPACE?: string; | ||||||
|   PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; |   PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; | ||||||
|   STRATUM_ENABLED: boolean; |  | ||||||
|   SERVICES_API?: string; |   SERVICES_API?: string; | ||||||
|   customize?: Customization; |   customize?: Customization; | ||||||
|   PROD_DOMAINS: string[]; |   PROD_DOMAINS: string[]; | ||||||
| @ -124,7 +123,6 @@ const defaultEnv: Env = { | |||||||
|   'ACCELERATOR_BUTTON': true, |   'ACCELERATOR_BUTTON': true, | ||||||
|   'PUBLIC_ACCELERATIONS': false, |   'PUBLIC_ACCELERATIONS': false, | ||||||
|   'ADDITIONAL_CURRENCIES': false, |   'ADDITIONAL_CURRENCIES': false, | ||||||
|   'STRATUM_ENABLED': false, |  | ||||||
|   'SERVICES_API': 'https://mempool.space/api/v1/services', |   'SERVICES_API': 'https://mempool.space/api/v1/services', | ||||||
|   'PROD_DOMAINS': [], |   'PROD_DOMAINS': [], | ||||||
| }; | }; | ||||||
| @ -161,8 +159,6 @@ export class StateService { | |||||||
|   liveMempoolBlockTransactions$: Observable<{ block: number, transactions: { [txid: string]: TransactionStripped} }>; |   liveMempoolBlockTransactions$: Observable<{ block: number, transactions: { [txid: string]: TransactionStripped} }>; | ||||||
|   accelerations$ = new Subject<AccelerationDelta>(); |   accelerations$ = new Subject<AccelerationDelta>(); | ||||||
|   liveAccelerations$: Observable<Acceleration[]>; |   liveAccelerations$: Observable<Acceleration[]>; | ||||||
|   stratumJobUpdate$ = new Subject<{ state: Record<string, StratumJob> } | { job: StratumJob }>(); |  | ||||||
|   stratumJobs$ = new BehaviorSubject<Record<string, StratumJob>>({}); |  | ||||||
|   txConfirmed$ = new Subject<[string, BlockExtended]>(); |   txConfirmed$ = new Subject<[string, BlockExtended]>(); | ||||||
|   txReplaced$ = new Subject<ReplacedTransaction>(); |   txReplaced$ = new Subject<ReplacedTransaction>(); | ||||||
|   txRbfInfo$ = new Subject<RbfTree>(); |   txRbfInfo$ = new Subject<RbfTree>(); | ||||||
| @ -307,24 +303,6 @@ export class StateService { | |||||||
|       map((accMap) => Object.values(accMap).sort((a,b) => b.added - a.added)) |       map((accMap) => Object.values(accMap).sort((a,b) => b.added - a.added)) | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     this.stratumJobUpdate$.pipe( |  | ||||||
|       scan((acc: Record<string, StratumJob>, update: { state: Record<string, StratumJob> } | { job: StratumJob }) => { |  | ||||||
|         if ('state' in update) { |  | ||||||
|           // Replace the entire state
 |  | ||||||
|           return update.state; |  | ||||||
|         } else { |  | ||||||
|           // Update or create a single job entry
 |  | ||||||
|           return { |  | ||||||
|             ...acc, |  | ||||||
|             [update.job.pool]: update.job |  | ||||||
|           }; |  | ||||||
|         } |  | ||||||
|       }, {}), |  | ||||||
|       shareReplay(1) |  | ||||||
|     ).subscribe(val => { |  | ||||||
|       this.stratumJobs$.next(val); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     this.networkChanged$.subscribe((network) => { |     this.networkChanged$.subscribe((network) => { | ||||||
|       this.transactions$ = new BehaviorSubject<TransactionStripped[]>(null); |       this.transactions$ = new BehaviorSubject<TransactionStripped[]>(null); | ||||||
|       this.blocksSubject$.next([]); |       this.blocksSubject$.next([]); | ||||||
|  | |||||||
| @ -36,7 +36,6 @@ export class WebsocketService { | |||||||
|   private isTrackingAccelerations: boolean = false; |   private isTrackingAccelerations: boolean = false; | ||||||
|   private isTrackingWallet: boolean = false; |   private isTrackingWallet: boolean = false; | ||||||
|   private trackingWalletName: string; |   private trackingWalletName: string; | ||||||
|   private isTrackingStratum: string | number | false = false; |  | ||||||
|   private trackingMempoolBlock: number; |   private trackingMempoolBlock: number; | ||||||
|   private trackingMempoolBlockNetwork: string; |   private trackingMempoolBlockNetwork: string; | ||||||
|   private stoppingTrackMempoolBlock: any | null = null; |   private stoppingTrackMempoolBlock: any | null = null; | ||||||
| @ -144,9 +143,6 @@ export class WebsocketService { | |||||||
|           if (this.isTrackingWallet) { |           if (this.isTrackingWallet) { | ||||||
|             this.startTrackingWallet(this.trackingWalletName); |             this.startTrackingWallet(this.trackingWalletName); | ||||||
|           } |           } | ||||||
|           if (this.isTrackingStratum !== false) { |  | ||||||
|             this.startTrackStratum(this.isTrackingStratum); |  | ||||||
|           } |  | ||||||
|           this.stateService.connectionState$.next(2); |           this.stateService.connectionState$.next(2); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -293,18 +289,6 @@ export class WebsocketService { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   startTrackStratum(pool: number | string) { |  | ||||||
|     this.websocketSubject.next({ 'track-stratum': pool }); |  | ||||||
|     this.isTrackingStratum = pool; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   stopTrackStratum() { |  | ||||||
|     if (this.isTrackingStratum) { |  | ||||||
|       this.websocketSubject.next({ 'track-stratum': null }); |  | ||||||
|       this.isTrackingStratum = false; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   fetchStatistics(historicalDate: string) { |   fetchStatistics(historicalDate: string) { | ||||||
|     this.websocketSubject.next({ historicalDate }); |     this.websocketSubject.next({ historicalDate }); | ||||||
|   } |   } | ||||||
| @ -528,14 +512,6 @@ export class WebsocketService { | |||||||
|       this.stateService.previousRetarget$.next(response.previousRetarget); |       this.stateService.previousRetarget$.next(response.previousRetarget); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (response.stratumJobs) { |  | ||||||
|       this.stateService.stratumJobUpdate$.next({ state: response.stratumJobs }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (response.stratumJob) { |  | ||||||
|       this.stateService.stratumJobUpdate$.next({ job: response.stratumJob }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (response['tomahawk']) { |     if (response['tomahawk']) { | ||||||
|       this.stateService.serverHealth$.next(response['tomahawk']); |       this.stateService.serverHealth$.next(response['tomahawk']); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -11,9 +11,9 @@ | |||||||
| <ng-template [ngIf]="!hideUnconfirmed && !confirmations && replaced"> | <ng-template [ngIf]="!hideUnconfirmed && !confirmations && replaced"> | ||||||
|   <button type="button" class="btn btn-sm btn-warning no-cursor {{buttonClass}}" i18n="transaction.replaced|Transaction replaced state">Replaced</button> |   <button type="button" class="btn btn-sm btn-warning no-cursor {{buttonClass}}" i18n="transaction.replaced|Transaction replaced state">Replaced</button> | ||||||
| </ng-template> | </ng-template> | ||||||
| <ng-template [ngIf]="!hideUnconfirmed && !confirmations && !replaced && (removed || cached)"> | <ng-template [ngIf]="!hideUnconfirmed && !confirmations && !replaced && removed"> | ||||||
|   <button type="button" class="btn btn-sm btn-warning no-cursor {{buttonClass}}" i18n="transaction.audit.removed|Transaction removed state">Removed</button> |   <button type="button" class="btn btn-sm btn-warning no-cursor {{buttonClass}}" i18n="transaction.audit.removed|Transaction removed state">Removed</button> | ||||||
| </ng-template> | </ng-template> | ||||||
| <ng-template [ngIf]="!hideUnconfirmed && chainTip != null && !confirmations && !replaced && !(removed || cached)"> | <ng-template [ngIf]="!hideUnconfirmed && chainTip != null && !confirmations && !replaced && !removed"> | ||||||
|   <button type="button" class="btn btn-sm btn-danger no-cursor {{buttonClass}}" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button> |   <button type="button" class="btn btn-sm btn-danger no-cursor {{buttonClass}}" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button> | ||||||
| </ng-template> | </ng-template> | ||||||
| @ -12,7 +12,6 @@ export class ConfirmationsComponent implements OnChanges { | |||||||
|   @Input() height: number; |   @Input() height: number; | ||||||
|   @Input() replaced: boolean = false; |   @Input() replaced: boolean = false; | ||||||
|   @Input() removed: boolean = false; |   @Input() removed: boolean = false; | ||||||
|   @Input() cached: boolean = false; |  | ||||||
|   @Input() hideUnconfirmed: boolean = false; |   @Input() hideUnconfirmed: boolean = false; | ||||||
|   @Input() buttonClass: string = ''; |   @Input() buttonClass: string = ''; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -26,7 +26,6 @@ export const MempoolErrors = { | |||||||
|   'unauthorized': `You are not authorized to do this`, |   'unauthorized': `You are not authorized to do this`, | ||||||
|   'faucet_too_soon': `You cannot request any more coins right now. Try again later.`, |   'faucet_too_soon': `You cannot request any more coins right now. Try again later.`, | ||||||
|   'faucet_not_available': `The faucet is not available right now. Try again later.`, |   'faucet_not_available': `The faucet is not available right now. Try again later.`, | ||||||
|   'faucet_not_available_no_utxo': `The faucet is not available right now. Please try again once a new block has been mined.`, |  | ||||||
|   'faucet_maximum_reached': `You are not allowed to request more coins`, |   'faucet_maximum_reached': `You are not allowed to request more coins`, | ||||||
|   'faucet_address_not_allowed': `You cannot use this address`, |   'faucet_address_not_allowed': `You cannot use this address`, | ||||||
|   'faucet_below_minimum': `Requested amount is too small`, |   'faucet_below_minimum': `Requested amount is too small`, | ||||||
|  | |||||||
| @ -4,10 +4,7 @@ import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstra | |||||||
| import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; | import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; | ||||||
| import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle, | import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle, | ||||||
|   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, |   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, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, faCircleXmark, faCalendarCheck, faMoneyBillTrendUp } from '@fortawesome/free-solid-svg-icons'; | ||||||
|   faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, |  | ||||||
|   faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, |  | ||||||
|   faCircleXmark, faCalendarCheck, faMoneyBillTrendUp, faRobot, faShareNodes, faCreditCard } from '@fortawesome/free-solid-svg-icons'; |  | ||||||
| import { InfiniteScrollModule } from 'ngx-infinite-scroll'; | import { InfiniteScrollModule } from 'ngx-infinite-scroll'; | ||||||
| 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'; | ||||||
| @ -83,7 +80,6 @@ import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe'; | |||||||
| import { DifficultyAdjustmentsTable } from '@components/difficulty-adjustments-table/difficulty-adjustments-table.components'; | import { DifficultyAdjustmentsTable } from '@components/difficulty-adjustments-table/difficulty-adjustments-table.components'; | ||||||
| import { BlocksList } from '@components/blocks-list/blocks-list.component'; | import { BlocksList } from '@components/blocks-list/blocks-list.component'; | ||||||
| import { RbfList } from '@components/rbf-list/rbf-list.component'; | import { RbfList } from '@components/rbf-list/rbf-list.component'; | ||||||
| import { StratumList } from '@components/stratum/stratum-list/stratum-list.component'; |  | ||||||
| import { RewardStatsComponent } from '@components/reward-stats/reward-stats.component'; | import { RewardStatsComponent } from '@components/reward-stats/reward-stats.component'; | ||||||
| import { DataCyDirective } from '@app/data-cy.directive'; | import { DataCyDirective } from '@app/data-cy.directive'; | ||||||
| import { LoadingIndicatorComponent } from '@components/loading-indicator/loading-indicator.component'; | import { LoadingIndicatorComponent } from '@components/loading-indicator/loading-indicator.component'; | ||||||
| @ -125,7 +121,6 @@ import { TwitterLogin } from '@components/twitter-login/twitter-login.component' | |||||||
| import { BitcoinInvoiceComponent } from '@components/bitcoin-invoice/bitcoin-invoice.component'; | import { BitcoinInvoiceComponent } from '@components/bitcoin-invoice/bitcoin-invoice.component'; | ||||||
| 
 | 
 | ||||||
| import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/weight-directives/weight-directives'; | import { OnlyVsizeDirective, OnlyWeightDirective } from '@app/shared/components/weight-directives/weight-directives'; | ||||||
| import { GithubLogin } from '../components/github-login.component/github-login.component'; |  | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|   declarations: [ |   declarations: [ | ||||||
| @ -203,7 +198,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c | |||||||
|     DifficultyAdjustmentsTable, |     DifficultyAdjustmentsTable, | ||||||
|     BlocksList, |     BlocksList, | ||||||
|     RbfList, |     RbfList, | ||||||
|     StratumList, |  | ||||||
|     DataCyDirective, |     DataCyDirective, | ||||||
|     RewardStatsComponent, |     RewardStatsComponent, | ||||||
|     LoadingIndicatorComponent, |     LoadingIndicatorComponent, | ||||||
| @ -243,7 +237,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c | |||||||
|     TwitterWidgetComponent, |     TwitterWidgetComponent, | ||||||
|     FaucetComponent, |     FaucetComponent, | ||||||
|     TwitterLogin, |     TwitterLogin, | ||||||
|     GithubLogin, |  | ||||||
|     BitcoinInvoiceComponent, |     BitcoinInvoiceComponent, | ||||||
|   ], |   ], | ||||||
|   imports: [ |   imports: [ | ||||||
| @ -349,7 +342,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c | |||||||
|     AmountShortenerPipe, |     AmountShortenerPipe, | ||||||
|     DifficultyAdjustmentsTable, |     DifficultyAdjustmentsTable, | ||||||
|     BlocksList, |     BlocksList, | ||||||
|     StratumList, |  | ||||||
|     DataCyDirective, |     DataCyDirective, | ||||||
|     RewardStatsComponent, |     RewardStatsComponent, | ||||||
|     LoadingIndicatorComponent, |     LoadingIndicatorComponent, | ||||||
| @ -378,7 +370,6 @@ import { GithubLogin } from '../components/github-login.component/github-login.c | |||||||
|     HttpErrorComponent, |     HttpErrorComponent, | ||||||
|     TwitterWidgetComponent, |     TwitterWidgetComponent, | ||||||
|     TwitterLogin, |     TwitterLogin, | ||||||
|     GithubLogin, |  | ||||||
|     BitcoinInvoiceComponent, |     BitcoinInvoiceComponent, | ||||||
|     BitcoinsatoshisPipe, |     BitcoinsatoshisPipe, | ||||||
| 
 | 
 | ||||||
| @ -461,8 +452,5 @@ export class SharedModule { | |||||||
|     library.addIcons(faCircleXmark); |     library.addIcons(faCircleXmark); | ||||||
|     library.addIcons(faCalendarCheck); |     library.addIcons(faCalendarCheck); | ||||||
|     library.addIcons(faMoneyBillTrendUp); |     library.addIcons(faMoneyBillTrendUp); | ||||||
|     library.addIcons(faRobot); |  | ||||||
|     library.addIcons(faShareNodes); |  | ||||||
|     library.addIcons(faCreditCard); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1567,7 +1567,7 @@ | |||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="bdb8bbb38e4ca3c73e19dc4167fbe4aec316f818" datatype="html"> |       <trans-unit id="bdb8bbb38e4ca3c73e19dc4167fbe4aec316f818" datatype="html"> | ||||||
|         <source>Total Bid Boost</source> |         <source>Total Bid Boost</source> | ||||||
|         <target>Augmentation totale des frais</target> |         <target>Total frais ajoutés</target> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html</context> |           <context context-type="sourcefile">src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html</context> | ||||||
|           <context context-type="linenumber">11</context> |           <context context-type="linenumber">11</context> | ||||||
| @ -1728,7 +1728,7 @@ | |||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="57cde27765d527a0d9195212fa5a7ce06408c827" datatype="html"> |       <trans-unit id="57cde27765d527a0d9195212fa5a7ce06408c827" datatype="html"> | ||||||
|         <source>Bid Boost</source> |         <source>Bid Boost</source> | ||||||
|         <target>Augmentation des frais</target> |         <target>Frais ajoutés</target> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/acceleration/accelerations-list/accelerations-list.component.html</context> |           <context context-type="sourcefile">src/app/components/acceleration/accelerations-list/accelerations-list.component.html</context> | ||||||
|           <context context-type="linenumber">17</context> |           <context context-type="linenumber">17</context> | ||||||
| @ -6739,7 +6739,7 @@ | |||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="date-base.just-now" datatype="html"> |       <trans-unit id="date-base.just-now" datatype="html"> | ||||||
|         <source>Just now</source> |         <source>Just now</source> | ||||||
|         <target>Juste maintenant</target> |         <target>À l'instant</target> | ||||||
|         <context-group purpose="location"> |         <context-group purpose="location"> | ||||||
|           <context context-type="sourcefile">src/app/components/time/time.component.ts</context> |           <context context-type="sourcefile">src/app/components/time/time.component.ts</context> | ||||||
|           <context context-type="linenumber">111</context> |           <context context-type="linenumber">111</context> | ||||||
|  | |||||||
| @ -150,8 +150,7 @@ http { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	server { | 	server { | ||||||
| 		listen 80; | 		listen 127.0.0.1:80; | ||||||
| 		listen [::]:80 |  | ||||||
| 		include /etc/nginx/nginx-mempool.conf; | 		include /etc/nginx/nginx-mempool.conf; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,7 +15,6 @@ whitelist=127.0.0.1 | |||||||
| whitelist=103.99.168.0/22 | whitelist=103.99.168.0/22 | ||||||
| whitelist=2401:b140::/32 | whitelist=2401:b140::/32 | ||||||
| blocksxor=0 | blocksxor=0 | ||||||
| logtimemicros=1 |  | ||||||
| #uacomment=@wiz | #uacomment=@wiz | ||||||
| 
 | 
 | ||||||
| [main] | [main] | ||||||
|  | |||||||
| @ -26,14 +26,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/elements/socket/esplora-elements-liquid", |     "UNIX_SOCKET_PATH": "/elements/socket/esplora-elements-liquid", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3001", |       "http://node201.fmt.mempool.space:3001", | ||||||
|       "http://node202.hnl.mempool.space:3001", |       "http://node202.fmt.mempool.space:3001", | ||||||
|       "http://node203.hnl.mempool.space:3001", |       "http://node203.fmt.mempool.space:3001", | ||||||
|       "http://node204.hnl.mempool.space:3001", |       "http://node204.fmt.mempool.space:3001", | ||||||
|       "http://node201.sg1.mempool.space:3001", |       "http://node205.fmt.mempool.space:3001", | ||||||
|       "http://node202.sg1.mempool.space:3001", |       "http://node206.fmt.mempool.space:3001", | ||||||
|       "http://node203.sg1.mempool.space:3001", |  | ||||||
|       "http://node204.sg1.mempool.space:3001", |  | ||||||
|       "http://node201.va1.mempool.space:3001", |       "http://node201.va1.mempool.space:3001", | ||||||
|       "http://node202.va1.mempool.space:3001", |       "http://node202.va1.mempool.space:3001", | ||||||
|       "http://node203.va1.mempool.space:3001", |       "http://node203.va1.mempool.space:3001", | ||||||
|  | |||||||
| @ -26,14 +26,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/elements/socket/esplora-elements-liquidtestnet", |     "UNIX_SOCKET_PATH": "/elements/socket/esplora-elements-liquidtestnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3004", |       "http://node201.fmt.mempool.space:3004", | ||||||
|       "http://node202.hnl.mempool.space:3004", |       "http://node202.fmt.mempool.space:3004", | ||||||
|       "http://node203.hnl.mempool.space:3004", |       "http://node203.fmt.mempool.space:3004", | ||||||
|       "http://node204.hnl.mempool.space:3004", |       "http://node204.fmt.mempool.space:3004", | ||||||
|       "http://node201.sg1.mempool.space:3004", |       "http://node205.fmt.mempool.space:3004", | ||||||
|       "http://node202.sg1.mempool.space:3004", |       "http://node206.fmt.mempool.space:3004", | ||||||
|       "http://node203.sg1.mempool.space:3004", |  | ||||||
|       "http://node204.sg1.mempool.space:3004", |  | ||||||
|       "http://node201.va1.mempool.space:3004", |       "http://node201.va1.mempool.space:3004", | ||||||
|       "http://node202.va1.mempool.space:3004", |       "http://node202.va1.mempool.space:3004", | ||||||
|       "http://node203.va1.mempool.space:3004", |       "http://node203.va1.mempool.space:3004", | ||||||
|  | |||||||
| @ -19,14 +19,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3000", |       "http://node201.fmt.mempool.space:3000", | ||||||
|       "http://node202.hnl.mempool.space:3000", |       "http://node202.fmt.mempool.space:3000", | ||||||
|       "http://node203.hnl.mempool.space:3000", |       "http://node203.fmt.mempool.space:3000", | ||||||
|       "http://node204.hnl.mempool.space:3000", |       "http://node204.fmt.mempool.space:3000", | ||||||
|       "http://node201.sg1.mempool.space:3000", |       "http://node205.fmt.mempool.space:3000", | ||||||
|       "http://node202.sg1.mempool.space:3000", |       "http://node206.fmt.mempool.space:3000", | ||||||
|       "http://node203.sg1.mempool.space:3000", |  | ||||||
|       "http://node204.sg1.mempool.space:3000", |  | ||||||
|       "http://node201.va1.mempool.space:3000", |       "http://node201.va1.mempool.space:3000", | ||||||
|       "http://node202.va1.mempool.space:3000", |       "http://node202.va1.mempool.space:3000", | ||||||
|       "http://node203.va1.mempool.space:3000", |       "http://node203.va1.mempool.space:3000", | ||||||
|  | |||||||
| @ -30,8 +30,7 @@ | |||||||
|   "CORE_RPC": { |   "CORE_RPC": { | ||||||
|     "PORT": 8332, |     "PORT": 8332, | ||||||
|     "USERNAME": "__BITCOIN_RPC_USER__", |     "USERNAME": "__BITCOIN_RPC_USER__", | ||||||
|     "PASSWORD": "__BITCOIN_RPC_PASS__", |     "PASSWORD": "__BITCOIN_RPC_PASS__" | ||||||
|     "DEBUG_LOG_PATH": "/bitcoin/debug.log" |  | ||||||
|   }, |   }, | ||||||
|   "SECOND_CORE_RPC": { |   "SECOND_CORE_RPC": { | ||||||
|     "PORT": 8302, |     "PORT": 8302, | ||||||
| @ -41,14 +40,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-mainnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3000", |       "http://node201.fmt.mempool.space:3000", | ||||||
|       "http://node202.hnl.mempool.space:3000", |       "http://node202.fmt.mempool.space:3000", | ||||||
|       "http://node203.hnl.mempool.space:3000", |       "http://node203.fmt.mempool.space:3000", | ||||||
|       "http://node204.hnl.mempool.space:3000", |       "http://node204.fmt.mempool.space:3000", | ||||||
|       "http://node201.sg1.mempool.space:3000", |       "http://node205.fmt.mempool.space:3000", | ||||||
|       "http://node202.sg1.mempool.space:3000", |       "http://node206.fmt.mempool.space:3000", | ||||||
|       "http://node203.sg1.mempool.space:3000", |  | ||||||
|       "http://node204.sg1.mempool.space:3000", |  | ||||||
|       "http://node201.va1.mempool.space:3000", |       "http://node201.va1.mempool.space:3000", | ||||||
|       "http://node202.va1.mempool.space:3000", |       "http://node202.va1.mempool.space:3000", | ||||||
|       "http://node203.va1.mempool.space:3000", |       "http://node203.va1.mempool.space:3000", | ||||||
| @ -104,14 +101,12 @@ | |||||||
|     "STATISTICS": true, |     "STATISTICS": true, | ||||||
|     "STATISTICS_START_TIME": "24h", |     "STATISTICS_START_TIME": "24h", | ||||||
|     "SERVERS": [ |     "SERVERS": [ | ||||||
|       "node201.hnl.mempool.space", |       "node201.fmt.mempool.space", | ||||||
|       "node202.hnl.mempool.space", |       "node202.fmt.mempool.space", | ||||||
|       "node203.hnl.mempool.space", |       "node203.fmt.mempool.space", | ||||||
|       "node204.hnl.mempool.space", |       "node204.fmt.mempool.space", | ||||||
|       "node201.sg1.mempool.space", |       "node205.fmt.mempool.space", | ||||||
|       "node202.sg1.mempool.space", |       "node206.fmt.mempool.space", | ||||||
|       "node203.sg1.mempool.space", |  | ||||||
|       "node204.sg1.mempool.space", |  | ||||||
|       "node201.va1.mempool.space", |       "node201.va1.mempool.space", | ||||||
|       "node202.va1.mempool.space", |       "node202.va1.mempool.space", | ||||||
|       "node203.va1.mempool.space", |       "node203.va1.mempool.space", | ||||||
| @ -159,9 +154,5 @@ | |||||||
|   "WALLETS": { |   "WALLETS": { | ||||||
|     "ENABLED": true, |     "ENABLED": true, | ||||||
|     "WALLETS": ["BITB", "3350"] |     "WALLETS": ["BITB", "3350"] | ||||||
|   }, |  | ||||||
|   "STRATUM": { |  | ||||||
|     "ENABLED": true, |  | ||||||
|     "API": "http://127.0.0.1:81/api/v1/stratum/ws" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,14 +19,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3003", |       "http://node201.fmt.mempool.space:3003", | ||||||
|       "http://node202.hnl.mempool.space:3003", |       "http://node202.fmt.mempool.space:3003", | ||||||
|       "http://node203.hnl.mempool.space:3003", |       "http://node203.fmt.mempool.space:3003", | ||||||
|       "http://node204.hnl.mempool.space:3003", |       "http://node204.fmt.mempool.space:3003", | ||||||
|       "http://node201.sg1.mempool.space:3003", |       "http://node205.fmt.mempool.space:3003", | ||||||
|       "http://node202.sg1.mempool.space:3003", |       "http://node206.fmt.mempool.space:3003", | ||||||
|       "http://node203.sg1.mempool.space:3003", |  | ||||||
|       "http://node204.sg1.mempool.space:3003", |  | ||||||
|       "http://node201.va1.mempool.space:3003", |       "http://node201.va1.mempool.space:3003", | ||||||
|       "http://node202.va1.mempool.space:3003", |       "http://node202.va1.mempool.space:3003", | ||||||
|       "http://node203.va1.mempool.space:3003", |       "http://node203.va1.mempool.space:3003", | ||||||
|  | |||||||
| @ -28,14 +28,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-signet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3003", |       "http://node201.fmt.mempool.space:3003", | ||||||
|       "http://node202.hnl.mempool.space:3003", |       "http://node202.fmt.mempool.space:3003", | ||||||
|       "http://node203.hnl.mempool.space:3003", |       "http://node203.fmt.mempool.space:3003", | ||||||
|       "http://node204.hnl.mempool.space:3003", |       "http://node204.fmt.mempool.space:3003", | ||||||
|       "http://node201.sg1.mempool.space:3003", |       "http://node205.fmt.mempool.space:3003", | ||||||
|       "http://node202.sg1.mempool.space:3003", |       "http://node206.fmt.mempool.space:3003", | ||||||
|       "http://node203.sg1.mempool.space:3003", |  | ||||||
|       "http://node204.sg1.mempool.space:3003", |  | ||||||
|       "http://node201.va1.mempool.space:3003", |       "http://node201.va1.mempool.space:3003", | ||||||
|       "http://node202.va1.mempool.space:3003", |       "http://node202.va1.mempool.space:3003", | ||||||
|       "http://node203.va1.mempool.space:3003", |       "http://node203.va1.mempool.space:3003", | ||||||
|  | |||||||
| @ -19,14 +19,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3002", |       "http://node201.fmt.mempool.space:3002", | ||||||
|       "http://node202.hnl.mempool.space:3002", |       "http://node202.fmt.mempool.space:3002", | ||||||
|       "http://node203.hnl.mempool.space:3002", |       "http://node203.fmt.mempool.space:3002", | ||||||
|       "http://node204.hnl.mempool.space:3002", |       "http://node204.fmt.mempool.space:3002", | ||||||
|       "http://node201.sg1.mempool.space:3002", |       "http://node205.fmt.mempool.space:3002", | ||||||
|       "http://node202.sg1.mempool.space:3002", |       "http://node206.fmt.mempool.space:3002", | ||||||
|       "http://node203.sg1.mempool.space:3002", |  | ||||||
|       "http://node204.sg1.mempool.space:3002", |  | ||||||
|       "http://node201.va1.mempool.space:3002", |       "http://node201.va1.mempool.space:3002", | ||||||
|       "http://node202.va1.mempool.space:3002", |       "http://node202.va1.mempool.space:3002", | ||||||
|       "http://node203.va1.mempool.space:3002", |       "http://node203.va1.mempool.space:3002", | ||||||
|  | |||||||
| @ -28,14 +28,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3002", |       "http://node201.fmt.mempool.space:3002", | ||||||
|       "http://node202.hnl.mempool.space:3002", |       "http://node202.fmt.mempool.space:3002", | ||||||
|       "http://node203.hnl.mempool.space:3002", |       "http://node203.fmt.mempool.space:3002", | ||||||
|       "http://node204.hnl.mempool.space:3002", |       "http://node204.fmt.mempool.space:3002", | ||||||
|       "http://node201.sg1.mempool.space:3002", |       "http://node205.fmt.mempool.space:3002", | ||||||
|       "http://node202.sg1.mempool.space:3002", |       "http://node206.fmt.mempool.space:3002", | ||||||
|       "http://node203.sg1.mempool.space:3002", |  | ||||||
|       "http://node204.sg1.mempool.space:3002", |  | ||||||
|       "http://node201.va1.mempool.space:3002", |       "http://node201.va1.mempool.space:3002", | ||||||
|       "http://node202.va1.mempool.space:3002", |       "http://node202.va1.mempool.space:3002", | ||||||
|       "http://node203.va1.mempool.space:3002", |       "http://node203.va1.mempool.space:3002", | ||||||
|  | |||||||
| @ -28,14 +28,12 @@ | |||||||
|   "ESPLORA": { |   "ESPLORA": { | ||||||
|     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet4", |     "UNIX_SOCKET_PATH": "/bitcoin/socket/esplora-bitcoin-testnet4", | ||||||
|     "FALLBACK": [ |     "FALLBACK": [ | ||||||
|       "http://node201.hnl.mempool.space:3005", |       "http://node201.fmt.mempool.space:3005", | ||||||
|       "http://node202.hnl.mempool.space:3005", |       "http://node202.fmt.mempool.space:3005", | ||||||
|       "http://node203.hnl.mempool.space:3005", |       "http://node203.fmt.mempool.space:3005", | ||||||
|       "http://node204.hnl.mempool.space:3005", |       "http://node204.fmt.mempool.space:3005", | ||||||
|       "http://node201.sg1.mempool.space:3005", |       "http://node205.fmt.mempool.space:3005", | ||||||
|       "http://node202.sg1.mempool.space:3005", |       "http://node206.fmt.mempool.space:3005", | ||||||
|       "http://node203.sg1.mempool.space:3005", |  | ||||||
|       "http://node204.sg1.mempool.space:3005", |  | ||||||
|       "http://node201.va1.mempool.space:3005", |       "http://node201.va1.mempool.space:3005", | ||||||
|       "http://node202.va1.mempool.space:3005", |       "http://node202.va1.mempool.space:3005", | ||||||
|       "http://node203.va1.mempool.space:3005", |       "http://node203.va1.mempool.space:3005", | ||||||
|  | |||||||
| @ -4,7 +4,8 @@ | |||||||
|   "TESTNET4_ENABLED": true, |   "TESTNET4_ENABLED": true, | ||||||
|   "LIQUID_ENABLED": false, |   "LIQUID_ENABLED": false, | ||||||
|   "LIQUID_TESTNET_ENABLED": false, |   "LIQUID_TESTNET_ENABLED": false, | ||||||
|   "STRATUM_ENABLED": true, |   "BISQ_ENABLED": true, | ||||||
|  |   "BISQ_SEPARATE_BACKEND": true, | ||||||
|   "SIGNET_ENABLED": true, |   "SIGNET_ENABLED": true, | ||||||
|   "MEMPOOL_WEBSITE_URL": "https://mempool.space", |   "MEMPOOL_WEBSITE_URL": "https://mempool.space", | ||||||
|   "LIQUID_WEBSITE_URL": "https://liquid.network", |   "LIQUID_WEBSITE_URL": "https://liquid.network", | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| proxy_cache_path /var/cache/nginx/services keys_zone=services:200m levels=1:2 inactive=30d max_size=200m; | proxy_cache_path /var/cache/nginx/services keys_zone=services:200m levels=1:2 inactive=30d max_size=200m; | ||||||
| proxy_cache_path /var/cache/nginx/apihot keys_zone=apihot:200m levels=1:2 inactive=60m max_size=20m; | proxy_cache_path /var/cache/nginx/apihot keys_zone=apihot:200m levels=1:2 inactive=60m max_size=20m; | ||||||
| proxy_cache_path /var/cache/nginx/apiwarm keys_zone=apiwarm:200m levels=1:2 inactive=24h max_size=200m; | proxy_cache_path /var/cache/nginx/apiwarm keys_zone=apiwarm:200m levels=1:2 inactive=24h max_size=200m; | ||||||
| proxy_cache_path /var/cache/nginx/apinormal keys_zone=apinormal:500m levels=1:2 inactive=24h max_size=2000m; | proxy_cache_path /var/cache/nginx/apinormal keys_zone=apinormal:200m levels=1:2 inactive=30d max_size=2000m; | ||||||
| proxy_cache_path /var/cache/nginx/apicold keys_zone=apicold:200m levels=1:2 inactive=60d max_size=2000m; | proxy_cache_path /var/cache/nginx/apicold keys_zone=apicold:200m levels=1:2 inactive=60d max_size=2000m; | ||||||
| 
 | 
 | ||||||
| proxy_cache_path /var/cache/nginx/unfurler keys_zone=unfurler:200m levels=1:2 inactive=30d max_size=2000m; | proxy_cache_path /var/cache/nginx/unfurler keys_zone=unfurler:200m levels=1:2 inactive=30d max_size=2000m; | ||||||
|  | |||||||
| @ -140,8 +140,7 @@ location @mempool-api-v1-cache-normal { | |||||||
| 	proxy_cache_valid 200 2s; | 	proxy_cache_valid 200 2s; | ||||||
| 	proxy_redirect off; | 	proxy_redirect off; | ||||||
| 
 | 
 | ||||||
| 	# cache for 2 seconds on server, but send expires -1 so browser doesn't cache | 	expires 2s; | ||||||
| 	expires -1; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| location @mempool-api-v1-cache-disabled { | location @mempool-api-v1-cache-disabled { | ||||||
|  | |||||||
							
								
								
									
										191
									
								
								rust/gbt/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										191
									
								
								rust/gbt/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1,21 +1,21 @@ | |||||||
| # This file is automatically @generated by Cargo. | # This file is automatically @generated by Cargo. | ||||||
| # It is not intended for manual editing. | # It is not intended for manual editing. | ||||||
| version = 4 | version = 3 | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "addr2line" | name = "addr2line" | ||||||
| version = "0.24.2" | version = "0.22.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" | checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "gimli", |  "gimli", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "adler2" | name = "adler" | ||||||
| version = "2.0.0" | version = "1.0.2" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "aho-corasick" | name = "aho-corasick" | ||||||
| @ -28,42 +28,48 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "autocfg" | name = "autocfg" | ||||||
| version = "1.4.0" | version = "1.3.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "backtrace" | name = "backtrace" | ||||||
| version = "0.3.74" | version = "0.3.73" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" | checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "addr2line", |  "addr2line", | ||||||
|  |  "cc", | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  "libc", |  "libc", | ||||||
|  "miniz_oxide", |  "miniz_oxide", | ||||||
|  "object", |  "object", | ||||||
|  "rustc-demangle", |  "rustc-demangle", | ||||||
|  "windows-targets", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "bitflags" | name = "bitflags" | ||||||
| version = "2.8.0" | version = "2.6.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "bytemuck" | name = "bytemuck" | ||||||
| version = "1.21.0" | version = "1.16.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "bytes" | name = "bytes" | ||||||
| version = "1.9.0" | version = "1.6.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" | checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "cc" | ||||||
|  | version = "1.1.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "cfg-if" | name = "cfg-if" | ||||||
| @ -82,9 +88,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ctor" | name = "ctor" | ||||||
| version = "0.2.9" | version = "0.2.8" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" | checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "quote", |  "quote", | ||||||
|  "syn", |  "syn", | ||||||
| @ -113,21 +119,27 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "gimli" | name = "gimli" | ||||||
| version = "0.31.1" | version = "0.29.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" | checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "hashbrown" | name = "hashbrown" | ||||||
| version = "0.15.2" | version = "0.14.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "hermit-abi" | ||||||
|  | version = "0.3.9" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "indexmap" | name = "indexmap" | ||||||
| version = "2.7.1" | version = "2.2.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "equivalent", |  "equivalent", | ||||||
|  "hashbrown", |  "hashbrown", | ||||||
| @ -141,15 +153,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "libc" | name = "libc" | ||||||
| version = "0.2.169" | version = "0.2.155" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "libloading" | name = "libloading" | ||||||
| version = "0.8.6" | version = "0.8.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" | checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  "windows-targets", |  "windows-targets", | ||||||
| @ -157,9 +169,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "log" | name = "log" | ||||||
| version = "0.4.25" | version = "0.4.22" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "matchers" | name = "matchers" | ||||||
| @ -178,18 +190,18 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "miniz_oxide" | name = "miniz_oxide" | ||||||
| version = "0.8.3" | version = "0.7.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "adler2", |  "adler", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "napi" | name = "napi" | ||||||
| version = "2.16.13" | version = "2.16.8" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "214f07a80874bb96a8433b3cdfc84980d56c7b02e1a0d7ba4ba0db5cef785e2b" | checksum = "a1bd081bbaef43600fd2c5dd4c525b8ecea7dfdacf40ebc674e87851dce6559e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags", |  "bitflags", | ||||||
|  "ctor", |  "ctor", | ||||||
| @ -201,15 +213,15 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "napi-build" | name = "napi-build" | ||||||
| version = "2.1.4" | version = "2.1.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "db836caddef23662b94e16bf1f26c40eceb09d6aee5d5b06a7ac199320b69b19" | checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "napi-derive" | name = "napi-derive" | ||||||
| version = "2.16.13" | version = "2.16.9" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c" | checksum = "87c3b5d4ab13e20a4bb9d3a1e2f3d4e77eee4a205d0f810abfd226b971dc6ce5" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  "convert_case", |  "convert_case", | ||||||
| @ -221,9 +233,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "napi-derive-backend" | name = "napi-derive-backend" | ||||||
| version = "1.0.75" | version = "1.0.71" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf" | checksum = "96de436a6ab93265beef838f8333c8345438f059df6081fe0ad0b8648ee0c524" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "convert_case", |  "convert_case", | ||||||
|  "once_cell", |  "once_cell", | ||||||
| @ -254,19 +266,29 @@ dependencies = [ | |||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "object" | name = "num_cpus" | ||||||
| version = "0.36.7" | version = "1.16.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" | ||||||
|  | dependencies = [ | ||||||
|  |  "hermit-abi", | ||||||
|  |  "libc", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "object" | ||||||
|  | version = "0.36.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "memchr", |  "memchr", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "once_cell" | name = "once_cell" | ||||||
| version = "1.20.2" | version = "1.19.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "overload" | name = "overload" | ||||||
| @ -276,15 +298,15 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pin-project-lite" | name = "pin-project-lite" | ||||||
| version = "0.2.16" | version = "0.2.14" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "priority-queue" | name = "priority-queue" | ||||||
| version = "2.1.1" | version = "2.0.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d" | checksum = "70c501afe3a2e25c9bd219aa56ec1e04cdb3fcdd763055be268778c13fa82c1f" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "autocfg", |  "autocfg", | ||||||
|  "equivalent", |  "equivalent", | ||||||
| @ -293,32 +315,32 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "proc-macro2" | name = "proc-macro2" | ||||||
| version = "1.0.93" | version = "1.0.86" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "unicode-ident", |  "unicode-ident", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "quote" | name = "quote" | ||||||
| version = "1.0.38" | version = "1.0.36" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "regex" | name = "regex" | ||||||
| version = "1.11.1" | version = "1.10.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" | checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "aho-corasick", |  "aho-corasick", | ||||||
|  "memchr", |  "memchr", | ||||||
|  "regex-automata 0.4.9", |  "regex-automata 0.4.7", | ||||||
|  "regex-syntax 0.8.5", |  "regex-syntax 0.8.4", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -332,13 +354,13 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "regex-automata" | name = "regex-automata" | ||||||
| version = "0.4.9" | version = "0.4.7" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "aho-corasick", |  "aho-corasick", | ||||||
|  "memchr", |  "memchr", | ||||||
|  "regex-syntax 0.8.5", |  "regex-syntax 0.8.4", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -349,9 +371,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "regex-syntax" | name = "regex-syntax" | ||||||
| version = "0.8.5" | version = "0.8.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "rustc-demangle" | name = "rustc-demangle" | ||||||
| @ -361,9 +383,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "semver" | name = "semver" | ||||||
| version = "1.0.25" | version = "1.0.23" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "sharded-slab" | name = "sharded-slab" | ||||||
| @ -382,9 +404,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "syn" | name = "syn" | ||||||
| version = "2.0.96" | version = "2.0.71" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" | checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
| @ -403,19 +425,20 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tokio" | name = "tokio" | ||||||
| version = "1.43.0" | version = "1.38.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" | checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "backtrace", |  "backtrace", | ||||||
|  |  "num_cpus", | ||||||
|  "pin-project-lite", |  "pin-project-lite", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tracing" | name = "tracing" | ||||||
| version = "0.1.41" | version = "0.1.40" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "pin-project-lite", |  "pin-project-lite", | ||||||
|  "tracing-attributes", |  "tracing-attributes", | ||||||
| @ -424,9 +447,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tracing-attributes" | name = "tracing-attributes" | ||||||
| version = "0.1.28" | version = "0.1.27" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
| @ -435,9 +458,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tracing-core" | name = "tracing-core" | ||||||
| version = "0.1.33" | version = "0.1.32" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "once_cell", |  "once_cell", | ||||||
|  "valuable", |  "valuable", | ||||||
| @ -456,9 +479,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tracing-subscriber" | name = "tracing-subscriber" | ||||||
| version = "0.3.19" | version = "0.3.18" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "matchers", |  "matchers", | ||||||
|  "nu-ansi-term", |  "nu-ansi-term", | ||||||
| @ -474,21 +497,21 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-ident" | name = "unicode-ident" | ||||||
| version = "1.0.15" | version = "1.0.12" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-segmentation" | name = "unicode-segmentation" | ||||||
| version = "1.12.0" | version = "1.11.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "valuable" | name = "valuable" | ||||||
| version = "0.1.1" | version = "0.1.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "winapi" | name = "winapi" | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| 1.84 | 1.79 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user