diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d2fc387f..68ffa840a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,16 +3,19 @@ name: CI Pipeline for the Backend and Frontend on: pull_request: types: [opened, review_requested, synchronize] + push: + branches: + - master jobs: backend: - if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" + if: "(github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')) || github.event_name == 'push'" strategy: matrix: - node: ["20", "21"] + node: ["22"] flavor: ["dev", "prod"] fail-fast: false - runs-on: "ubuntu-latest" + runs-on: ubuntu-latest name: Backend (${{ matrix.flavor }}) - node ${{ matrix.node }} steps: @@ -66,7 +69,7 @@ jobs: cache: name: "Cache assets for builds" - runs-on: "ubuntu-latest" + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 @@ -157,13 +160,13 @@ jobs: frontend: needs: cache - if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" + if: "(github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')) || github.event_name == 'push'" strategy: matrix: - node: ["20", "21"] + node: ["22"] flavor: ["dev", "prod"] fail-fast: false - runs-on: "ubuntu-latest" + runs-on: ubuntu-latest name: Frontend (${{ matrix.flavor }}) - node ${{ matrix.node }} steps: @@ -245,8 +248,8 @@ jobs: VERBOSE: 1 e2e: - if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" - runs-on: "ubuntu-latest" + if: "(github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')) || github.event_name == 'push'" + runs-on: ubuntu-latest needs: frontend strategy: fail-fast: false @@ -263,7 +266,7 @@ jobs: - name: Setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 22 cache: "npm" cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json @@ -309,7 +312,7 @@ jobs: tag: ${{ github.event_name }} working-directory: ${{ matrix.module }}/frontend build: npm run config:defaults:${{ matrix.module }} - start: npm run start:local-staging + start: npm run start:parameterized wait-on: "http://localhost:4200" wait-on-timeout: 120 record: true @@ -334,7 +337,7 @@ jobs: tag: ${{ github.event_name }} working-directory: ${{ matrix.module }}/frontend build: npm run config:defaults:${{ matrix.module }} - start: npm run start:local-staging + start: npm run start:parameterized wait-on: "http://localhost:4200" wait-on-timeout: 120 record: true @@ -359,7 +362,7 @@ jobs: tag: ${{ github.event_name }} working-directory: ${{ matrix.module }}/frontend build: npm run config:defaults:mempool - start: npm run start:local-staging + start: npm run start:parameterized wait-on: "http://localhost:4200" wait-on-timeout: 120 record: true @@ -375,10 +378,9 @@ jobs: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} - validate_docker_json: - if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" - runs-on: "ubuntu-latest" + if: "(github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')) || github.event_name == 'push'" + runs-on: ubuntu-latest name: Validate generated backend Docker JSON steps: diff --git a/.github/workflows/e2e_parameterized.yml b/.github/workflows/e2e_parameterized.yml new file mode 100644 index 000000000..a53677a80 --- /dev/null +++ b/.github/workflows/e2e_parameterized.yml @@ -0,0 +1,273 @@ +name: 'Parameterized e2e tests' + +on: + workflow_dispatch: + inputs: + ref: + description: 'Branch name or Pull Request number (e.g., master or 6102)' + required: true + default: 'master' + type: string + mempool_hostname: + description: 'Mempool Hostname' + required: true + default: 'mempool.space' + type: string + liquid_hostname: + description: 'Liquid Hostname' + required: true + default: 'liquid.network' + type: string + +jobs: + cache: + name: "Cache assets for builds" + runs-on: ubuntu-latest + steps: + - name: Determine checkout ref + id: determine-ref + run: | + REF_INPUT="${{ github.event.inputs.ref }}" + if [[ "$REF_INPUT" =~ ^[0-9]+$ ]]; then + echo "ref=refs/pull/$REF_INPUT/head" >> $GITHUB_OUTPUT + else + echo "ref=$REF_INPUT" >> $GITHUB_OUTPUT + fi + + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ steps.determine-ref.outputs.ref }} + path: assets + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + registry-url: "https://registry.npmjs.org" + + - name: Install (Prod dependencies only) + run: npm ci --omit=dev --omit=optional + working-directory: assets/frontend + + - name: Restore cached mining pool assets + continue-on-error: true + id: cache-mining-pool-restore + uses: actions/cache/restore@v4 + with: + path: | + mining-pool-assets.zip + key: mining-pool-assets-cache + + - name: Restore promo video assets + continue-on-error: true + id: cache-promo-video-restore + uses: actions/cache/restore@v4 + with: + path: | + promo-video-assets.zip + key: promo-video-assets-cache + + - name: Unzip assets before building (src/resources) + continue-on-error: true + run: unzip -o mining-pool-assets.zip -d assets/frontend/src/resources/mining-pools + + - name: Unzip assets before building (src/resources) + continue-on-error: true + run: unzip -o promo-video-assets.zip -d assets/frontend/src/resources/promo-video + + # - name: Unzip assets before building (dist) + # continue-on-error: true + # run: unzip assets.zip -d assets/frontend/dist/mempool/browser/resources + + - name: Sync-assets + run: npm run sync-assets-dev + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MEMPOOL_CDN: 1 + VERBOSE: 1 + working-directory: assets/frontend + + - name: Zip mining-pool assets + run: zip -jrq mining-pool-assets.zip assets/frontend/src/resources/mining-pools/* + + - name: Zip promo-video assets + run: zip -jrq promo-video-assets.zip assets/frontend/src/resources/promo-video/* + + - name: Upload mining pool assets as artifact + uses: actions/upload-artifact@v4 + with: + name: mining-pool-assets + path: mining-pool-assets.zip + + - name: Upload promo video assets as artifact + uses: actions/upload-artifact@v4 + with: + name: promo-video-assets + path: promo-video-assets.zip + + - name: Save mining pool assets cache + id: cache-mining-pool-save + uses: actions/cache/save@v4 + with: + path: | + mining-pool-assets.zip + key: mining-pool-assets-cache + + - name: Save promo video assets cache + id: cache-promo-video-save + uses: actions/cache/save@v4 + with: + path: | + promo-video-assets.zip + key: promo-video-assets-cache + + e2e: + runs-on: ubuntu-latest + needs: cache + strategy: + fail-fast: false + matrix: + module: ["mempool", "liquid", "testnet4"] + + name: E2E tests for ${{ matrix.module }} + steps: + - name: Determine checkout ref + id: determine-ref + run: | + REF_INPUT="${{ github.event.inputs.ref }}" + if [[ "$REF_INPUT" =~ ^[0-9]+$ ]]; then + echo "ref=refs/pull/$REF_INPUT/head" >> $GITHUB_OUTPUT + else + echo "ref=$REF_INPUT" >> $GITHUB_OUTPUT + fi + + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ steps.determine-ref.outputs.ref }} + path: ${{ matrix.module }} + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 22 + cache: "npm" + cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json + + - name: Restore cached mining pool assets + continue-on-error: true + id: cache-mining-pool-restore + uses: actions/cache/restore@v4 + with: + path: | + mining-pool-assets.zip + key: mining-pool-assets-cache + + - name: Restore cached promo video assets + continue-on-error: true + id: cache-promo-video-restore + uses: actions/cache/restore@v4 + with: + path: | + promo-video-assets.zip + key: promo-video-assets-cache + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: mining-pool-assets + + - name: Unzip assets before building (src/resources) + run: unzip -o mining-pool-assets.zip -d ${{ matrix.module }}/frontend/src/resources/mining-pools + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: promo-video-assets + + - name: Unzip assets before building (src/resources) + run: unzip -o promo-video-assets.zip -d ${{ matrix.module }}/frontend/src/resources/promo-video + + # mempool + - name: Chrome browser tests (${{ matrix.module }}) + if: ${{ matrix.module == 'mempool' }} + 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:parameterized + wait-on: "http://localhost:4200" + wait-on-timeout: 120 + record: true + parallel: true + spec: | + cypress/e2e/mainnet/*.spec.ts + cypress/e2e/signet/*.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_name }} (${{ github.sha }}) - ref ${{ github.event.inputs.ref }} - ${{ github.event.inputs.mempool_hostname }} - ${{ github.event.inputs.liquid_hostname }} + MEMPOOL_HOSTNAME: ${{ github.event.inputs.mempool_hostname }} + LIQUID_HOSTNAME: ${{ github.event.inputs.liquid_hostname }} + MEMPOOL_CI_API_KEY: ${{ secrets.MEMPOOL_CI_API_KEY }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + 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:parameterized + 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_name }} (${{ github.sha }}) - ref ${{ github.event.inputs.ref }} - ${{ github.event.inputs.mempool_hostname }} - ${{ github.event.inputs.liquid_hostname }} + MEMPOOL_HOSTNAME: ${{ github.event.inputs.mempool_hostname }} + LIQUID_HOSTNAME: ${{ github.event.inputs.liquid_hostname }} + MEMPOOL_CI_API_KEY: ${{ secrets.MEMPOOL_CI_API_KEY }} + 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:parameterized + 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: + COMMIT_INFO_MESSAGE: ${{ github.event_name }} (${{ github.sha }}) - ref ${{ github.event.inputs.ref }} - ${{ github.event.inputs.mempool_hostname }} - ${{ github.event.inputs.liquid_hostname }} + MEMPOOL_HOSTNAME: ${{ github.event.inputs.mempool_hostname }} + LIQUID_HOSTNAME: ${{ github.event.inputs.liquid_hostname }} + MEMPOOL_CI_API_KEY: ${{ secrets.MEMPOOL_CI_API_KEY }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} diff --git a/.github/workflows/get_backend_block_height.yml b/.github/workflows/get_backend_block_height.yml index 52f3b038c..ae30188e5 100644 --- a/.github/workflows/get_backend_block_height.yml +++ b/.github/workflows/get_backend_block_height.yml @@ -4,7 +4,7 @@ on: [workflow_dispatch] jobs: print-backend-sha: - runs-on: 'ubuntu-latest' + runs-on: ubuntu-latest name: Get block height steps: - name: Checkout diff --git a/.github/workflows/get_backend_hash.yml b/.github/workflows/get_backend_hash.yml index 57950dee4..0e31735b6 100644 --- a/.github/workflows/get_backend_hash.yml +++ b/.github/workflows/get_backend_hash.yml @@ -4,7 +4,7 @@ on: [workflow_dispatch] jobs: print-backend-sha: - runs-on: 'ubuntu-latest' + runs-on: ubuntu-latest name: Print backend hashes steps: - name: Checkout diff --git a/.github/workflows/get_image_digest.yml b/.github/workflows/get_image_digest.yml index 7414eeb08..18ad39fde 100644 --- a/.github/workflows/get_image_digest.yml +++ b/.github/workflows/get_image_digest.yml @@ -10,7 +10,7 @@ on: type: string jobs: print-images-sha: - runs-on: 'ubuntu-latest' + runs-on: ubuntu-latest name: Print digest for images steps: - name: Checkout diff --git a/.nvmrc b/.nvmrc index a9b234d51..53d1c14db 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.8.0 +v22 diff --git a/LICENSE b/LICENSE index 1c368c00a..b6e67e523 100644 --- a/LICENSE +++ b/LICENSE @@ -10,7 +10,7 @@ However, this copyright license does not include an implied right or license to use any trademarks, service marks, logos, or trade names of Mempool Space K.K. or any other contributor to The Mempool Open Source Project. -The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, +The Mempool Open Source Project®, Mempool Accelerator®, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem™, Mempool Goggles™, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 73a14ba4e..3cf2923f1 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -406,8 +406,8 @@ class BitcoinRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * cacheDuration).toUTCString()); res.json(block); - } catch (e) { - handleError(req, res, 500, 'Failed to get block'); + } catch (e: any) { + handleError(req, res, e?.response?.status === 404 ? 404 : 500, 'Failed to get block'); } } diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 581850277..d475470db 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -1469,11 +1469,11 @@ class 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)`); + logger.debug(`Unable to retrieve 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})`); + logger.debug(`Unable to retrieve list of blocks.definition_hash from db (exception: ${e})`); return null; } } @@ -1484,11 +1484,11 @@ class Blocks { 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)`); + logger.debug(`Unable to retrieve 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})`); + logger.debug(`Unable to retrieve list of blocks for definition hash ${definitionHash} from db (exception: ${e})`); return null; } } diff --git a/backend/src/api/services/wallets.ts b/backend/src/api/services/wallets.ts index dd4d7ebc9..f498a80ad 100644 --- a/backend/src/api/services/wallets.ts +++ b/backend/src/api/services/wallets.ts @@ -30,6 +30,7 @@ const POLL_FREQUENCY = 5 * 60 * 1000; // 5 minutes class WalletApi { private wallets: Record = {}; private syncing = false; + private lastSync = 0; constructor() { this.wallets = config.WALLETS.ENABLED ? (config.WALLETS.WALLETS as string[]).reduce((acc, wallet) => { @@ -47,7 +48,38 @@ class WalletApi { if (!config.WALLETS.ENABLED || this.syncing) { return; } + this.syncing = true; + + if (config.WALLETS.AUTO && (Date.now() - this.lastSync) > POLL_FREQUENCY) { + try { + // update list of active wallets + this.lastSync = Date.now(); + const response = await axios.get(config.MEMPOOL_SERVICES.API + `/wallets`); + const walletList: string[] = response.data; + if (walletList) { + // create a quick lookup dictionary of active wallets + const newWallets: Record = Object.fromEntries( + walletList.map(wallet => [wallet, true]) + ); + for (const wallet of walletList) { + // don't overwrite existing wallets + if (!this.wallets[wallet]) { + this.wallets[wallet] = { name: wallet, addresses: {}, lastPoll: 0 }; + } + } + // remove wallets that are no longer active + for (const wallet of Object.keys(this.wallets)) { + if (!newWallets[wallet]) { + delete this.wallets[wallet]; + } + } + } + } catch (e) { + logger.err(`Error updating active wallets: ${(e instanceof Error ? e.message : e)}`); + } + } + for (const walletKey of Object.keys(this.wallets)) { const wallet = this.wallets[walletKey]; if (wallet.lastPoll < (Date.now() - POLL_FREQUENCY)) { @@ -72,6 +104,7 @@ class WalletApi { } } } + this.syncing = false; } diff --git a/backend/src/config.ts b/backend/src/config.ts index a1050a7d5..3fe3db2ee 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -164,6 +164,7 @@ interface IConfig { }, WALLETS: { ENABLED: boolean; + AUTO: boolean; WALLETS: string[]; }, STRATUM: { @@ -334,6 +335,7 @@ const defaults: IConfig = { }, 'WALLETS': { 'ENABLED': false, + 'AUTO': false, 'WALLETS': [], }, 'STRATUM': { diff --git a/backend/src/index.ts b/backend/src/index.ts index 1b2204c28..d8fc90f4a 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -131,6 +131,9 @@ class Server { this.app .use((req: Request, res: Response, next: NextFunction) => { res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With'); + res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count,X-Mempool-Auth'); next(); }) .use(express.urlencoded({ extended: true })) diff --git a/backend/src/repositories/HashratesRepository.ts b/backend/src/repositories/HashratesRepository.ts index ec44afebe..93aa2d53f 100644 --- a/backend/src/repositories/HashratesRepository.ts +++ b/backend/src/repositories/HashratesRepository.ts @@ -93,7 +93,7 @@ class HashratesRepository { const [rows]: any[] = await DB.query(query); return rows.map(row => row.timestamp); } catch (e) { - logger.err('Cannot retreive indexed weekly hashrate timestamps. Reason: ' + (e instanceof Error ? e.message : e), logger.tags.mining); + logger.err('Cannot retrieve indexed weekly hashrate timestamps. Reason: ' + (e instanceof Error ? e.message : e), logger.tags.mining); throw e; } } diff --git a/frontend/angular.json b/frontend/angular.json index 3aa1cb6a8..c5da6a09a 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -261,20 +261,14 @@ "proxyConfig": "proxy.conf.mixed.js", "verbose": true }, - "staging": { - "proxyConfig": "proxy.conf.js", - "disableHostCheck": true, - "host": "0.0.0.0", - "verbose": true - }, "local-prod": { "proxyConfig": "proxy.conf.js", "disableHostCheck": true, "host": "0.0.0.0", "verbose": false }, - "local-staging": { - "proxyConfig": "proxy.conf.staging.js", + "parameterized": { + "proxyConfig": "proxy.conf.parameterized.js", "disableHostCheck": true, "host": "0.0.0.0", "verbose": false @@ -371,4 +365,4 @@ } } } -} +} \ No newline at end of file diff --git a/frontend/custom-sv-config.json b/frontend/custom-sv-config.json index dee3dab18..9a61704a2 100644 --- a/frontend/custom-sv-config.json +++ b/frontend/custom-sv-config.json @@ -16,10 +16,10 @@ "mobileOrder": 4 }, { - "component": "balance", + "component": "walletBalance", "mobileOrder": 1, "props": { - "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo" + "wallet": "ONBTC" } }, { @@ -30,21 +30,22 @@ } }, { - "component": "address", + "component": "wallet", "mobileOrder": 2, "props": { - "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo", - "period": "1m" + "wallet": "ONBTC", + "period": "1m", + "label": "bitcoin.gob.sv" } }, { "component": "blocks" }, { - "component": "addressTransactions", + "component": "walletTransactions", "mobileOrder": 3, "props": { - "address": "32ixEdVJWo3kmvJGMTZq5jAQVZZeuwnqzo" + "wallet": "ONBTC" } } ] diff --git a/frontend/cypress/e2e/liquid/liquid.spec.ts b/frontend/cypress/e2e/liquid/liquid.spec.ts index c7d2a92ee..4fb7431d9 100644 --- a/frontend/cypress/e2e/liquid/liquid.spec.ts +++ b/frontend/cypress/e2e/liquid/liquid.spec.ts @@ -57,11 +57,6 @@ describe('Liquid', () => { }); }); - it('loads the tv page - desktop', () => { - cy.visit(`${basePath}/tv`); - cy.waitForSkeletonGone(); - }); - it('loads the graphs page - mobile', () => { cy.visit(`${basePath}`) cy.waitForSkeletonGone(); diff --git a/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts b/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts index 54e355ce8..7befda49f 100644 --- a/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts +++ b/frontend/cypress/e2e/liquidtestnet/liquidtestnet.spec.ts @@ -57,11 +57,6 @@ describe('Liquid Testnet', () => { cy.waitForSkeletonGone(); }); - it('loads the tv page - desktop', () => { - cy.visit(`${basePath}/tv`); - cy.waitForSkeletonGone(); - }); - it('loads the graphs page - mobile', () => { cy.visit(`${basePath}`) cy.waitForSkeletonGone(); diff --git a/frontend/cypress/e2e/mainnet/mainnet.spec.ts b/frontend/cypress/e2e/mainnet/mainnet.spec.ts index 7e17c09cd..a664f333c 100644 --- a/frontend/cypress/e2e/mainnet/mainnet.spec.ts +++ b/frontend/cypress/e2e/mainnet/mainnet.spec.ts @@ -1,4 +1,4 @@ -import { emitMempoolInfo, dropWebSocket } from '../../support/websocket'; +import { emitMempoolInfo, dropWebSocket, receiveWebSocketMessageFromServer } from '../../support/websocket'; const baseModule = Cypress.env('BASE_MODULE'); @@ -216,6 +216,69 @@ describe('Mainnet', () => { cy.get('[data-cy="tx-1"] .table-tx-vout .highlight').its('length').should('equal', 2); cy.get('[data-cy="tx-1"] .table-tx-vout .highlight').invoke('text').should('contain', `${address}`); }); + + describe('address poisoning', () => { + it('highlights potential address poisoning attacks on outputs, prefix and infix', () => { + const txid = '152a5dea805f95d6f83e50a9fd082630f542a52a076ebabdb295723eaf53fa30'; + const prefix = '1DonatePLease'; + const infix1 = 'SenderAddressXVXCmAY'; + const infix2 = '5btcToSenderXXWBoKhB'; + + cy.visit(`/tx/${txid}`); + cy.waitForSkeletonGone(); + cy.get('.alert-mempool').should('exist'); + cy.get('.poison-alert').its('length').should('equal', 2); + + cy.get('.prefix') + .should('have.length', 2) + .each(($el) => { + cy.wrap($el).should('have.text', prefix); + }); + + cy.get('.infix') + .should('have.length', 2) + .then(($infixes) => { + cy.wrap($infixes[0]).should('have.text', infix1); + cy.wrap($infixes[1]).should('have.text', infix2); + }); + }); + + it('highlights potential address poisoning attacks on inputs and outputs, prefix, infix and postfix', () => { + const txid = '44544516084ea916ff1eb69c675c693e252addbbaf77102ffff86e3979ac6132'; + const prefix = 'bc1qge8'; + const infix1 = '6gqjnk8aqs3nvv7ejrvcd4zq6qur3'; + const infix2 = 'xyxjex6zzzx5g8hh65vsel4e548p2'; + const postfix1 = '6p6e3r'; + const postfix2 = '6p6e3r'; + + cy.visit(`/tx/${txid}`); + cy.waitForSkeletonGone(); + cy.get('.alert-mempool').should('exist'); + cy.get('.poison-alert').its('length').should('equal', 2); + + cy.get('.prefix') + .should('have.length', 2) + .each(($el) => { + cy.wrap($el).should('have.text', prefix); + }); + + cy.get('.infix') + .should('have.length', 2) + .then(($infixes) => { + cy.wrap($infixes[0]).should('have.text', infix1); + cy.wrap($infixes[1]).should('have.text', infix2); + }); + + cy.get('.postfix') + .should('have.length', 2) + .then(($postfixes) => { + cy.wrap($postfixes[0]).should('include.text', postfix1); + cy.wrap($postfixes[1]).should('include.text', postfix2); + }); + + }); + }); + }); describe('blocks navigation', () => { @@ -397,6 +460,7 @@ describe('Mainnet', () => { cy.get('#dropdownFees').should('be.visible'); cy.get('.btn-group').should('be.visible'); }); + it('check buttons - tablet', () => { cy.viewport('ipad-2'); cy.visit('/graphs'); @@ -405,6 +469,7 @@ describe('Mainnet', () => { cy.get('#dropdownFees').should('be.visible'); cy.get('.btn-group').should('be.visible'); }); + it('check buttons - desktop', () => { cy.viewport('macbook-16'); cy.visit('/graphs'); @@ -415,26 +480,6 @@ describe('Mainnet', () => { }); }); - it('loads the tv screen - desktop', () => { - cy.viewport('macbook-16'); - cy.visit('/graphs/mempool'); - cy.waitForSkeletonGone(); - cy.get('#btn-tv').click().then(() => { - cy.viewport('macbook-16'); - cy.get('.chart-holder'); - cy.get('.blockchain-wrapper').should('be.visible'); - cy.get('#mempool-block-0').should('be.visible'); - }); - }); - - it('loads the tv screen - mobile', () => { - cy.viewport('iphone-6'); - cy.visit('/tv'); - cy.waitForSkeletonGone(); - cy.get('.chart-holder'); - cy.get('.blockchain-wrapper').should('not.visible'); - }); - it('loads the api screen', () => { cy.visit('/'); cy.waitForSkeletonGone(); @@ -516,7 +561,44 @@ describe('Mainnet', () => { }); describe('RBF transactions', () => { - it('shows RBF transactions properly (mobile)', () => { + it('RBF page gets updated over websockets', () => { + cy.intercept('/api/v1/replacements', { + statusCode: 200, + body: [] + }); + + cy.intercept('/api/v1/fullrbf/replacements', { + statusCode: 200, + body: [] + }); + + cy.mockMempoolSocketV2(); + + cy.visit('/rbf'); + cy.get('.no-replacements'); + cy.get('.tree').should('have.length', 0); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'rbf_page/rbf_01.json' + } + } + }); + + cy.get('.tree').should('have.length', 1); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'rbf_page/rbf_02.json' + } + } + }); + cy.get('.tree').should('have.length', 2); + }); + + it('shows RBF transactions properly (mobile - details)', () => { cy.intercept('/api/v1/tx/21518a98d1aa9df524865d2f88c578499f524eb1d0c4d3e70312ab863508692f/cached', { fixture: 'mainnet_tx_cached.json' }).as('cached_tx'); @@ -527,7 +609,7 @@ describe('Mainnet', () => { cy.viewport('iphone-xr'); cy.mockMempoolSocket(); - cy.visit('/tx/21518a98d1aa9df524865d2f88c578499f524eb1d0c4d3e70312ab863508692f'); + cy.visit('/tx/21518a98d1aa9df524865d2f88c578499f524eb1d0c4d3e70312ab863508692f?mode=details'); cy.waitForSkeletonGone(); @@ -545,7 +627,120 @@ describe('Mainnet', () => { } }); + cy.get('.alert-mempool').should('be.visible'); + }); + + it('shows RBF transactions properly (mobile - tracker)', () => { + cy.mockMempoolSocketV2(); + cy.viewport('iphone-xr'); + + // API Mocks + cy.intercept('/api/v1/mining/pools/1w', { + fixture: 'details_rbf/api_mining_pools_1w.json' + }).as('api_mining_1w'); + + cy.intercept('/api/tx/242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29', { + statusCode: 404, + body: 'Transaction not found' + }).as('api_tx01_404'); + + cy.intercept('/api/v1/tx/242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29/cached', { + fixture: 'details_rbf/tx01_api_cached.json' + }).as('api_tx01_cached'); + + cy.intercept('/api/v1/tx/242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29/rbf', { + fixture: 'details_rbf/tx01_api_rbf.json' + }).as('api_tx01_rbf'); + + cy.visit('/tx/242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29?mode=tracker'); + cy.wait('@api_tx01_rbf'); + + // Start sending mocked WS messages + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'details_rbf/tx01_ws_stratum_jobs.json' + } + } + }); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'details_rbf/tx01_ws_blocks_01.json' + } + } + }); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'details_rbf/tx01_ws_tx_replaced.json' + } + } + }); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'details_rbf/tx01_ws_mempool_blocks_01.json' + } + } + }); + cy.get('.alert-replaced').should('be.visible'); + cy.get('.explainer').should('be.visible'); + cy.get('svg[data-icon=timeline]').should('be.visible'); + + // Second TX setup + cy.intercept('/api/tx/b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698', { + fixture: 'details_rbf/tx02_api_tx.json' + }).as('tx02_api'); + + cy.intercept('/api/v1/transaction-times?txId%5B%5D=b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698', { + fixture: 'details_rbf/tx02_api_tx_times.json' + }).as('tx02_api_tx_times'); + + cy.intercept('/api/v1/tx/b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698/rbf', { + fixture: 'details_rbf/tx02_api_rbf.json' + }).as('tx02_api_rbf'); + + cy.intercept('/api/v1/cpfp/b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698', { + fixture: 'details_rbf/tx02_api_cpfp.json' + }).as('tx02_api_cpfp'); + + // Go to the replacement tx + cy.get('.alert-replaced a').click(); + + cy.wait('@tx02_api_cpfp'); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'details_rbf/tx02_ws_tx_position.json' + } + } + }); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'details_rbf/tx02_ws_mempool_blocks_01.json' + } + } + }); + + cy.get('svg[data-icon=hourglass-half]').should('be.visible'); + + receiveWebSocketMessageFromServer({ + params: { + file: { + path: 'details_rbf/tx02_ws_block.json' + } + } + }); + cy.get('app-confirmations'); + cy.get('svg[data-icon=circle-check]').should('be.visible'); }); it('shows RBF transactions properly (desktop)', () => { diff --git a/frontend/cypress/e2e/signet/signet.spec.ts b/frontend/cypress/e2e/signet/signet.spec.ts index 11c47d14d..ae591c6a7 100644 --- a/frontend/cypress/e2e/signet/signet.spec.ts +++ b/frontend/cypress/e2e/signet/signet.spec.ts @@ -60,30 +60,6 @@ describe('Signet', () => { }); }); - describe.skip('tv mode', () => { - it('loads the tv screen - desktop', () => { - cy.viewport('macbook-16'); - cy.visit('/signet/graphs'); - cy.waitForSkeletonGone(); - cy.get('#btn-tv').click().then(() => { - cy.get('.chart-holder').should('be.visible'); - cy.get('#mempool-block-0').should('be.visible'); - cy.get('.tv-only').should('not.exist'); - }); - }); - - it('loads the tv screen - mobile', () => { - cy.visit('/signet/graphs'); - cy.waitForSkeletonGone(); - cy.get('#btn-tv').click().then(() => { - cy.viewport('iphone-8'); - cy.get('.chart-holder').should('be.visible'); - cy.get('.tv-only').should('not.exist'); - cy.get('#mempool-block-0').should('be.visible'); - }); - }); - }); - it('loads the api screen', () => { cy.visit('/signet'); cy.waitForSkeletonGone(); diff --git a/frontend/cypress/e2e/testnet4/testnet4.spec.ts b/frontend/cypress/e2e/testnet4/testnet4.spec.ts index c67d2414b..97af0e08e 100644 --- a/frontend/cypress/e2e/testnet4/testnet4.spec.ts +++ b/frontend/cypress/e2e/testnet4/testnet4.spec.ts @@ -60,30 +60,6 @@ describe('Testnet4', () => { }); }); - describe('tv mode', () => { - it('loads the tv screen - desktop', () => { - cy.viewport('macbook-16'); - cy.visit('/testnet4/graphs'); - cy.waitForSkeletonGone(); - cy.get('#btn-tv').click().then(() => { - cy.wait(1000); - cy.get('.tv-only').should('not.exist'); - cy.get('#mempool-block-0').should('be.visible'); - }); - }); - - it('loads the tv screen - mobile', () => { - cy.visit('/testnet4/graphs'); - cy.waitForSkeletonGone(); - cy.get('#btn-tv').click().then(() => { - cy.viewport('iphone-6'); - cy.wait(1000); - cy.get('.tv-only').should('not.exist'); - }); - }); - }); - - it('loads the api screen', () => { cy.visit('/testnet4'); cy.waitForSkeletonGone(); diff --git a/frontend/cypress/fixtures/details_rbf/api_accelerator_estimate.json b/frontend/cypress/fixtures/details_rbf/api_accelerator_estimate.json new file mode 100644 index 000000000..889c5d763 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/api_accelerator_estimate.json @@ -0,0 +1,60 @@ +{ + "txSummary": { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "effectiveVsize": 224, + "effectiveFee": 960, + "ancestorCount": 1 + }, + "cost": 1000, + "targetFeeRate": 3, + "nextBlockFee": 672, + "userBalance": 0, + "mempoolBaseFee": 50000, + "vsizeFee": 0, + "pools": [ + 36, + 102, + 112, + 44, + 4, + 2, + 6, + 94, + 143, + 43, + 105, + 115, + 142, + 111 + ], + "options": [ + { + "fee": 1000 + }, + { + "fee": 2000 + }, + { + "fee": 10000 + } + ], + "hasAccess": false, + "availablePaymentMethods": { + "bitcoin": { + "enabled": true, + "min": 1000, + "max": 10000000 + }, + "applePay": { + "enabled": true, + "min": 10, + "max": 1000 + }, + "googlePay": { + "enabled": true, + "min": 10, + "max": 1000 + } + }, + "unavailable": false +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/api_accelerator_version.json b/frontend/cypress/fixtures/details_rbf/api_accelerator_version.json new file mode 100644 index 000000000..a01c899b8 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/api_accelerator_version.json @@ -0,0 +1,3 @@ +{ + "gitCommit": "62f80296" +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/api_block_history.json b/frontend/cypress/fixtures/details_rbf/api_block_history.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/api_block_history.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/api_mining_pools_1w.json b/frontend/cypress/fixtures/details_rbf/api_mining_pools_1w.json new file mode 100644 index 000000000..3a678ca02 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/api_mining_pools_1w.json @@ -0,0 +1,260 @@ +{ + "pools": [ + { + "poolId": 112, + "name": "Foundry USA", + "link": "https://foundrydigital.com", + "blockCount": 323, + "rank": 1, + "emptyBlocks": 0, + "slug": "foundryusa", + "avgMatchRate": 99.96, + "avgFeeDelta": "-0.01971455", + "poolUniqueId": 111 + }, + { + "poolId": 45, + "name": "AntPool", + "link": "https://www.antpool.com", + "blockCount": 171, + "rank": 2, + "emptyBlocks": 0, + "slug": "antpool", + "avgMatchRate": 99.99, + "avgFeeDelta": "-0.04227368", + "poolUniqueId": 44 + }, + { + "poolId": 74, + "name": "ViaBTC", + "link": "https://viabtc.com", + "blockCount": 166, + "rank": 3, + "emptyBlocks": 0, + "slug": "viabtc", + "avgMatchRate": 99.99, + "avgFeeDelta": "-0.02530964", + "poolUniqueId": 73 + }, + { + "poolId": 37, + "name": "F2Pool", + "link": "https://www.f2pool.com", + "blockCount": 104, + "rank": 4, + "emptyBlocks": 0, + "slug": "f2pool", + "avgMatchRate": 99.99, + "avgFeeDelta": "-0.03299327", + "poolUniqueId": 36 + }, + { + "poolId": 116, + "name": "MARA Pool", + "link": "https://marapool.com", + "blockCount": 66, + "rank": 5, + "emptyBlocks": 0, + "slug": "marapool", + "avgMatchRate": 99.97, + "avgFeeDelta": "0.02366061", + "poolUniqueId": 115 + }, + { + "poolId": 103, + "name": "SpiderPool", + "link": "https://www.spiderpool.com", + "blockCount": 46, + "rank": 6, + "emptyBlocks": 1, + "slug": "spiderpool", + "avgMatchRate": 97.82, + "avgFeeDelta": "-0.07258913", + "poolUniqueId": 102 + }, + { + "poolId": 142, + "name": "SECPOOL", + "link": "https://www.secpool.com", + "blockCount": 30, + "rank": 7, + "emptyBlocks": 1, + "slug": "secpool", + "avgMatchRate": 96.67, + "avgFeeDelta": "-0.06596000", + "poolUniqueId": 141 + }, + { + "poolId": 106, + "name": "Binance Pool", + "link": "https://pool.binance.com", + "blockCount": 28, + "rank": 8, + "emptyBlocks": 0, + "slug": "binancepool", + "avgMatchRate": 99.99, + "avgFeeDelta": "-0.05834286", + "poolUniqueId": 105 + }, + { + "poolId": 5, + "name": "Luxor", + "link": "https://mining.luxor.tech", + "blockCount": 28, + "rank": 9, + "emptyBlocks": 0, + "slug": "luxor", + "avgMatchRate": 100, + "avgFeeDelta": "-0.05496071", + "poolUniqueId": 4 + }, + { + "poolId": 143, + "name": "OCEAN", + "link": "https://ocean.xyz/", + "blockCount": 12, + "rank": 10, + "emptyBlocks": 0, + "slug": "ocean", + "avgMatchRate": 91.9, + "avgFeeDelta": "-0.14650833", + "poolUniqueId": 142 + }, + { + "poolId": 44, + "name": "Braiins Pool", + "link": "https://braiins.com/pool", + "blockCount": 12, + "rank": 11, + "emptyBlocks": 0, + "slug": "braiinspool", + "avgMatchRate": 100, + "avgFeeDelta": "-0.03553333", + "poolUniqueId": 43 + }, + { + "poolId": 113, + "name": "SBI Crypto", + "link": "https://sbicrypto.com", + "blockCount": 8, + "rank": 12, + "emptyBlocks": 0, + "slug": "sbicrypto", + "avgMatchRate": 98.65, + "avgFeeDelta": "-0.04246250", + "poolUniqueId": 112 + }, + { + "poolId": 152, + "name": "Carbon Negative", + "link": "https://github.com/bitcoin-data/mining-pools/issues/48", + "blockCount": 7, + "rank": 13, + "emptyBlocks": 0, + "slug": "carbonnegative", + "avgMatchRate": 99.75, + "avgFeeDelta": "-0.04407143", + "poolUniqueId": 151 + }, + { + "poolId": 7, + "name": "BTC.com", + "link": "https://pool.btc.com", + "blockCount": 5, + "rank": 14, + "emptyBlocks": 0, + "slug": "btccom", + "avgMatchRate": 99.98, + "avgFeeDelta": "-0.02496000", + "poolUniqueId": 6 + }, + { + "poolId": 162, + "name": "Mining Squared", + "link": "https://pool.bsquared.network/", + "blockCount": 4, + "rank": 15, + "emptyBlocks": 0, + "slug": "miningsquared", + "avgMatchRate": 100, + "avgFeeDelta": "-0.00915000", + "poolUniqueId": 161 + }, + { + "poolId": 95, + "name": "Poolin", + "link": "https://www.poolin.com", + "blockCount": 4, + "rank": 16, + "emptyBlocks": 0, + "slug": "poolin", + "avgMatchRate": 100, + "avgFeeDelta": "-0.26485000", + "poolUniqueId": 94 + }, + { + "poolId": 1, + "name": "Unknown", + "link": "https://learnmeabitcoin.com/technical/coinbase-transaction", + "blockCount": 4, + "rank": 17, + "emptyBlocks": 0, + "slug": "unknown", + "avgMatchRate": 100, + "avgFeeDelta": "-0.06490000", + "poolUniqueId": 0 + }, + { + "poolId": 144, + "name": "WhitePool", + "link": "https://whitebit.com/mining-pool", + "blockCount": 3, + "rank": 18, + "emptyBlocks": 0, + "slug": "whitepool", + "avgMatchRate": 100, + "avgFeeDelta": "-0.01293333", + "poolUniqueId": 143 + }, + { + "poolId": 3, + "name": "ULTIMUSPOOL", + "link": "https://www.ultimuspool.com", + "blockCount": 1, + "rank": 19, + "emptyBlocks": 0, + "slug": "ultimuspool", + "avgMatchRate": 100, + "avgFeeDelta": "-0.16130000", + "poolUniqueId": 2 + }, + { + "poolId": 50, + "name": "Solo CK", + "link": "https://solo.ckpool.org", + "blockCount": 1, + "rank": 20, + "emptyBlocks": 0, + "slug": "solock", + "avgMatchRate": 100, + "avgFeeDelta": "-0.01510000", + "poolUniqueId": 49 + }, + { + "poolId": 158, + "name": "BitFuFuPool", + "link": "https://www.bitfufu.com/pool", + "blockCount": 1, + "rank": 21, + "emptyBlocks": 0, + "slug": "bitfufupool", + "avgMatchRate": 100, + "avgFeeDelta": "-0.01630000", + "poolUniqueId": 157 + } + ], + "blockCount": 1024, + "lastEstimatedHashrate": 786391245138648900000, + "lastEstimatedHashrate3d": 797683179385121300000, + "lastEstimatedHashrate1w": 827836055441520300000 +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx01_api_cached.json b/frontend/cypress/fixtures/details_rbf/tx01_api_cached.json new file mode 100644 index 000000000..62184cc9d --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx01_api_cached.json @@ -0,0 +1,55 @@ +{ + "txid": "242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29", + "version": 2, + "locktime": 0, + "vin": [ + { + "txid": "fb16c141d22a3a7af5e44e7478a8663866c35d554cd85107ec8a99b97e5a72e9", + "vout": 0, + "prevout": { + "scriptpubkey": "76a914099f831c49289c7b9cc07a9a632867ecd51a105a88ac", + "scriptpubkey_asm": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 099f831c49289c7b9cc07a9a632867ecd51a105a OP_EQUALVERIFY OP_CHECKSIG", + "scriptpubkey_type": "p2pkh", + "scriptpubkey_address": "1stAprEZapjCYGUACUcXohQqSHF9MU5Kj", + "value": 50000 + }, + "scriptsig": "483045022100f44a50e894c30b28400933da120b872ff15bd2e66dd034ffbbb925fd054dd60f02206ba477a9da3fae68a9f420f53bc25493270bd987d13b75fef39cf514aea9cdb3014104ea83ffe426ee6b6827029c72fbdcb0a1602829c1fe384637a7d178aa62a6a1e3d7f29250e001ee708a93ca9771f50ee638aaaaef8941c8a7e2bad5494b23e0df", + "scriptsig_asm": "OP_PUSHBYTES_72 3045022100f44a50e894c30b28400933da120b872ff15bd2e66dd034ffbbb925fd054dd60f02206ba477a9da3fae68a9f420f53bc25493270bd987d13b75fef39cf514aea9cdb301 OP_PUSHBYTES_65 04ea83ffe426ee6b6827029c72fbdcb0a1602829c1fe384637a7d178aa62a6a1e3d7f29250e001ee708a93ca9771f50ee638aaaaef8941c8a7e2bad5494b23e0df", + "is_coinbase": false, + "sequence": 4294967293 + } + ], + "vout": [ + { + "scriptpubkey": "5120a2fc5cb445755389eed295ab9d594b0facd3e00840a3e477fa7c025412c53795", + "scriptpubkey_asm": "OP_PUSHNUM_1 OP_PUSHBYTES_32 a2fc5cb445755389eed295ab9d594b0facd3e00840a3e477fa7c025412c53795", + "scriptpubkey_type": "v1_p2tr", + "scriptpubkey_address": "bc1p5t79edz9w4fcnmkjjk4e6k2tp7kd8cqggz37gal60sp9gyk9x72sk4mk0f", + "value": 49394 + } + ], + "size": 233, + "weight": 932, + "sigops": 0, + "fee": 606, + "status": { + "confirmed": false + }, + "order": 701313494, + "vsize": 233, + "adjustedVsize": 233, + "feePerVsize": 2.6008583690987126, + "adjustedFeePerVsize": 2.6008583690987126, + "effectiveFeePerVsize": 2.6008583690987126, + "firstSeen": 1743541407, + "inputs": [], + "cpfpDirty": false, + "ancestors": [], + "descendants": [], + "bestDescendant": null, + "position": { + "block": 0, + "vsize": 318595.5 + }, + "flags": 1099511645193 +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx01_api_rbf.json b/frontend/cypress/fixtures/details_rbf/tx01_api_rbf.json new file mode 100644 index 000000000..5278f9ffe --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx01_api_rbf.json @@ -0,0 +1,34 @@ +{ + "replacements": { + "tx": { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "fee": 960, + "vsize": 224, + "value": 49040, + "rate": 4.285714285714286, + "time": 1743541726, + "rbf": true, + "fullRbf": false + }, + "time": 1743541726, + "fullRbf": false, + "replaces": [ + { + "tx": { + "txid": "242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29", + "fee": 606, + "vsize": 233, + "value": 49394, + "rate": 2.6008583690987126, + "time": 1743541407, + "rbf": true + }, + "time": 1743541407, + "interval": 319, + "fullRbf": false, + "replaces": [] + } + ] + }, + "replaces": null +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx01_ws_blocks_01.json b/frontend/cypress/fixtures/details_rbf/tx01_ws_blocks_01.json new file mode 100644 index 000000000..32b6578fc --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx01_ws_blocks_01.json @@ -0,0 +1,579 @@ +{ + "blocks": [ + { + "id": "0000000000000000000079f5b74b6533abb0b1ece06570d8d157b5bebd1460b4", + "height": 890440, + "version": 559235072, + "timestamp": 1743535677, + "bits": 386038124, + "nonce": 2920325684, + "difficulty": 113757508810854, + "merkle_root": "c793d5fdbfb1ebe99e14a13a6d65370057d311774d33c71da166663b18722474", + "tx_count": 3823, + "size": 1578209, + "weight": 3993461, + "previousblockhash": "000000000000000000020fb2e24425793e17e60e188205dc1694d221790348b2", + "mediantime": 1743532406, + "stale": false, + "extras": { + "reward": 319838750, + "coinbaseRaw": "0348960d082f5669614254432f2cfabe6d6d294719da11c017243828bf32c405341db7f19387fee92c25413c45e114907f9810000000000000001058bf9601429f9fa7a6c160d10d00000000000000", + "orphans": [], + "medianFee": 4, + "feeRange": [ + 3, + 3, + 3.0191082802547773, + 3.980952380952381, + 5, + 10, + 427.748502994012 + ], + "totalFees": 7338750, + "avgFee": 1920, + "avgFeeRate": 7, + "utxoSetChange": 4093, + "avgTxSize": 412.71000000000004, + "totalInputs": 7430, + "totalOutputs": 11523, + "totalOutputAmt": 547553568373, + "segwitTotalTxs": 3432, + "segwitTotalSize": 1467920, + "segwitTotalWeight": 3552413, + "feePercentiles": null, + "virtualSize": 998365.25, + "coinbaseAddress": "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4", + "coinbaseAddresses": [ + "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4" + ], + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 fb37342f6275b13936799def06f2eb4c0f201515 OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "\u0003H–\r\b/ViaBTC/,ú¾mm)G\u0019Ú\u0011À\u0017$8(¿2Ä\u00054\u001d·ñ“‡þé,%Aìg/Foundry USA Pool #dropgold/\u0001S\nEaç\u0002\u0000\u0000\u0000\u0000\u0000", + "header": "00a03220b46014bdbeb557d1d87065e0ecb1b0ab33654bb7f579000000000000000000003ed60f06cec16df4399b5dafa7077036c2eb58cc6a16e6cdca559b9e2f7e4525bb3eec676c790217b7b3c9cb", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 111, + "name": "Foundry USA", + "slug": "foundryusa", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 2792968, + "expectedWeight": 3991959, + "similarity": 0.9951416839808291 + } + }, + { + "id": "000000000000000000014845978a876b3d8bf5d489b9d87e88873952b06ddfce", + "height": 890442, + "version": 557981696, + "timestamp": 1743536834, + "bits": 386038124, + "nonce": 470697326, + "difficulty": 113757508810854, + "merkle_root": "5e92e681c1db2797a5b3e5016729059f8b60a256cafb51d835dac2b3964c0db4", + "tx_count": 3566, + "size": 1628328, + "weight": 3993552, + "previousblockhash": "00000000000000000000ef5a27459785ea4c91e05f64adfad306af6dfc0cd19c", + "mediantime": 1743532867, + "stale": false, + "extras": { + "reward": 318057766, + "coinbaseRaw": "034a960d194d696e656420627920416e74506f6f6c204d000201e15e2989fabe6d6dd599e9dfa40be51f1517c8f512c5c3d51c7656182f1df335d34b98ee02c527db080000000000000000004f92b702000000000000", + "orphans": [], + "medianFee": 3.00860164711668, + "feeRange": [ + 1.5174418604651163, + 2.0140845070422535, + 2.492354740061162, + 3, + 4.020942408376963, + 7, + 200 + ], + "totalFees": 5557766, + "avgFee": 1558, + "avgFeeRate": 5, + "utxoSetChange": 1971, + "avgTxSize": 456.48, + "totalInputs": 7938, + "totalOutputs": 9909, + "totalOutputAmt": 900044492230, + "segwitTotalTxs": 3214, + "segwitTotalSize": 1526463, + "segwitTotalWeight": 3586200, + "feePercentiles": null, + "virtualSize": 998388, + "coinbaseAddress": "37jKPSmbEGwgfacCr2nayn1wTaqMAbA94Z", + "coinbaseAddresses": [ + "37jKPSmbEGwgfacCr2nayn1wTaqMAbA94Z", + "39C7fxSzEACPjM78Z7xdPxhf7mKxJwvfMJ" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 42402a28dd61f2718a4b27ae72a4791d5bbdade7 OP_EQUAL", + "coinbaseSignatureAscii": "\u0003J–\r\u0019Mined by AntPool M\u0000\u0002\u0001á^)‰ú¾mmՙéߤ\u000bå\u001f\u0015\u0017Èõ\u0012ÅÃÕ\u001cvV\u0018/\u001dó5ÓK˜î\u0002Å'Û\b\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000O’·\u0002\u0000\u0000\u0000\u0000\u0000\u0000", + "header": "002042219cd10cfc6daf06d3faad645fe0914cea859745275aef00000000000000000000b40d4c96b3c2da35d851fbca56a2608b9f05296701e5b3a59727dbc181e6925ec242ec676c7902176e450e1c", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 44, + "name": "AntPool", + "slug": "antpool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 5764747, + "expectedWeight": 3991786, + "similarity": 0.9029319155137951 + } + }, + { + "id": "000000000000000000026e08b270834273511b353bed30d54706211adc96f5f6", + "height": 890443, + "version": 706666496, + "timestamp": 1743537197, + "bits": 386038124, + "nonce": 321696065, + "difficulty": 113757508810854, + "merkle_root": "3d7574f7eca741fa94b4690868a242e5b286f8a0417ad0275d4ab05893e96350", + "tx_count": 2155, + "size": 1700002, + "weight": 3993715, + "previousblockhash": "000000000000000000014845978a876b3d8bf5d489b9d87e88873952b06ddfce", + "mediantime": 1743533789, + "stale": false, + "extras": { + "reward": 315112344, + "coinbaseRaw": "034b960d21202020204d696e656420627920536563706f6f6c2020202070000b05e388958c01fabe6d6db7ae4bfa7b1294e16e800b4563f1f5ddeb5c0740319eba45600f3f05d2d7272910000000000000000000c2cb7e020000", + "orphans": [], + "medianFee": 1.4360674424569184, + "feeRange": [ + 1, + 1.0135135135135136, + 1.09717868338558, + 2.142857142857143, + 3.009584664536741, + 4.831858407079646, + 196.07843137254903 + ], + "totalFees": 2612344, + "avgFee": 1212, + "avgFeeRate": 2, + "utxoSetChange": -2880, + "avgTxSize": 788.64, + "totalInputs": 9773, + "totalOutputs": 6893, + "totalOutputAmt": 264603969671, + "segwitTotalTxs": 1933, + "segwitTotalSize": 1556223, + "segwitTotalWeight": 3418707, + "feePercentiles": null, + "virtualSize": 998428.75, + "coinbaseAddress": "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "coinbaseAddresses": [ + "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "3Awm3FNpmwrbvAFVThRUFqgpbVuqWisni9" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 8ee90177614ecde53314fd67c46162f315852a07 OP_EQUAL", + "coinbaseSignatureAscii": "\u0003K–\r! Mined by Secpool p\u0000\u000b\u0005㈕Œ\u0001ú¾mm·®Kú{\u0012”án€\u000bEcñõÝë\\\u0007@1žºE`\u000f?\u0005Ò×')\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000ÂË~\u0002\u0000\u0000", + "header": "00e01e2acedf6db0523987887ed8b989d4f58b3d6b878a974548010000000000000000005063e99358b04a5d27d07a41a0f886b2e542a2680869b494fa41a7ecf774753d2d44ec676c79021741b12c13", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 141, + "name": "SECPOOL", + "slug": "secpool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 2623934, + "expectedWeight": 3991917, + "similarity": 0.9951244468050102 + } + }, + { + "id": "00000000000000000001b36ec470ae4ec39f5d975f665d6f40e9d35bfa65290d", + "height": 890444, + "version": 671080448, + "timestamp": 1743539347, + "bits": 386038124, + "nonce": 994357124, + "difficulty": 113757508810854, + "merkle_root": "c891d4bf68e22916274b667eb3287d50da2ddd63f8dad892da045cc2ad4a7b21", + "tx_count": 3797, + "size": 1500309, + "weight": 3993525, + "previousblockhash": "000000000000000000026e08b270834273511b353bed30d54706211adc96f5f6", + "mediantime": 1743533986, + "stale": false, + "extras": { + "reward": 318708524, + "coinbaseRaw": "034c960d082f5669614254432f2cfabe6d6d45b7fd7ab53a0914da7dcc9d21fe44f0936f5354169a56df9d5139f07afbc2b41000000000000000106fc0eb03f0ac2e851d18d8d9f85ad70000000000", + "orphans": [], + "medianFee": 4.064775540157046, + "feeRange": [ + 3.014354066985646, + 3.18368700265252, + 3.602836879432624, + 4.231825525040388, + 5.581730769230769, + 10, + 697.7151162790698 + ], + "totalFees": 6208524, + "avgFee": 1635, + "avgFeeRate": 6, + "utxoSetChange": 5755, + "avgTxSize": 395.02, + "totalInputs": 6681, + "totalOutputs": 12436, + "totalOutputAmt": 835839828101, + "segwitTotalTxs": 3351, + "segwitTotalSize": 1354446, + "segwitTotalWeight": 3410181, + "feePercentiles": null, + "virtualSize": 998381.25, + "coinbaseAddress": "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4", + "coinbaseAddresses": [ + "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4" + ], + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 fb37342f6275b13936799def06f2eb4c0f201515 OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "\u0003L–\r\b/ViaBTC/,ú¾mmE·ýzµ:\t\u0014Ú}̝!þDð“oST\u0016šVߝQ9ðzû´\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0010oÀë\u0003ð¬.…\u001d\u0018ØÙøZ×\u0000\u0000\u0000\u0000\u0000", + "header": "00e0ff27f6f596dc1a210647d530ed3b351b5173428370b2086e02000000000000000000217b4aadc25c04da92d8daf863dd2dda507d28b37e664b271629e268bfd491c8934cec676c79021784af443b", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 73, + "name": "ViaBTC", + "slug": "viabtc", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 6253024, + "expectedWeight": 3991868, + "similarity": 0.9862862477811569 + } + }, + { + "id": "00000000000000000001402065f940b9475159bdc962c92a66d3c6652ffa338a", + "height": 890445, + "version": 601202688, + "timestamp": 1743539574, + "bits": 386038124, + "nonce": 1647397133, + "difficulty": 113757508810854, + "merkle_root": "61d8294afa8f6bafa4d979a77d187dee5f75a6392f957ea647d96eefbbbc5e9b", + "tx_count": 3579, + "size": 1659862, + "weight": 3993406, + "previousblockhash": "00000000000000000001b36ec470ae4ec39f5d975f665d6f40e9d35bfa65290d", + "mediantime": 1743535677, + "stale": false, + "extras": { + "reward": 315617086, + "coinbaseRaw": "034d960d04764dec672f466f756e6472792055534120506f6f6c202364726f70676f6c642f4fac7c451540000000000000", + "orphans": [], + "medianFee": 2.5565329189526835, + "feeRange": [ + 1.521613832853026, + 2, + 2.2411347517730498, + 3, + 3, + 3.954954954954955, + 162.78343949044586 + ], + "totalFees": 3117086, + "avgFee": 871, + "avgFeeRate": 3, + "utxoSetChange": 1881, + "avgTxSize": 463.65000000000003, + "totalInputs": 7893, + "totalOutputs": 9774, + "totalOutputAmt": 324878597485, + "segwitTotalTxs": 3189, + "segwitTotalSize": 1538741, + "segwitTotalWeight": 3509030, + "feePercentiles": null, + "virtualSize": 998351.5, + "coinbaseAddress": "bc1pp7w6kxnj7lzgm29pmuhezwl0vjdlcrthqukll5gn9xuqfq5n673smy4m63", + "coinbaseAddresses": [ + "bc1pp7w6kxnj7lzgm29pmuhezwl0vjdlcrthqukll5gn9xuqfq5n673smy4m63", + "bc1qwzrryqr3ja8w7hnja2spmkgfdcgvqwp5swz4af4ngsjecfz0w0pqud7k38" + ], + "coinbaseSignature": "OP_PUSHNUM_1 OP_PUSHBYTES_32 0f9dab1a72f7c48da8a1df2f913bef649bfc0d77072dffd11329b8048293d7a3", + "coinbaseSignatureAscii": "\u0003M–\r\u0004vMìg/Foundry USA Pool #dropgold/O¬|E\u0015@\u0000\u0000\u0000\u0000\u0000\u0000", + "header": "00a0d5230d2965fa5bd3e9406f5d665f975d9fc34eae70c46eb3010000000000000000009b5ebcbbef6ed947a67e952f39a6755fee7d187da779d9a4af6b8ffa4a29d861764dec676c7902170d493162", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 111, + "name": "Foundry USA", + "slug": "foundryusa", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 3145370, + "expectedWeight": 3991903, + "similarity": 0.9903353189076812 + } + }, + { + "id": "00000000000000000001fe3d26b02146d0fc2192db06cbc12478d4b347f5306b", + "height": 890446, + "version": 537722880, + "timestamp": 1743541107, + "bits": 386038124, + "nonce": 826569764, + "difficulty": 113757508810854, + "merkle_root": "d9b320d7cb5aace80ca20b934b13b4a272121fbdd59f3aaba690e0326ca2c144", + "tx_count": 3998, + "size": 1541360, + "weight": 3993545, + "previousblockhash": "00000000000000000001402065f940b9475159bdc962c92a66d3c6652ffa338a", + "mediantime": 1743535803, + "stale": false, + "extras": { + "reward": 317976882, + "coinbaseRaw": "034e960d20202020204d696e656420627920536563706f6f6c2020202070001b04fad5fdfefabe6d6d59dd8ebce6e5aab8fb943bbdcede474b6f2d00a395a717970104a6958c17f1ca100000000000000000008089c9350200", + "orphans": [], + "medianFee": 3.3750830641948864, + "feeRange": [ + 2.397163120567376, + 3, + 3, + 3.463647199046484, + 4.49438202247191, + 7.213930348258707, + 476.1904761904762 + ], + "totalFees": 5476882, + "avgFee": 1370, + "avgFeeRate": 5, + "utxoSetChange": 4951, + "avgTxSize": 385.41, + "totalInputs": 7054, + "totalOutputs": 12005, + "totalOutputAmt": 983289729453, + "segwitTotalTxs": 3538, + "segwitTotalSize": 1396505, + "segwitTotalWeight": 3414233, + "feePercentiles": null, + "virtualSize": 998386.25, + "coinbaseAddress": "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "coinbaseAddresses": [ + "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "3Awm3FNpmwrbvAFVThRUFqgpbVuqWisni9" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 8ee90177614ecde53314fd67c46162f315852a07 OP_EQUAL", + "coinbaseSignatureAscii": "\u0003N–\r Mined by Secpool p\u0000\u001b\u0004úÕýþú¾mmYݎ¼æåª¸û”;½ÎÞGKo-\u0000£•§\u0017—\u0001\u0004¦•Œ\u0017ñÊ\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000€‰É5\u0002\u0000", + "header": "00000d208a33fa2f65c6d3662ac962c9bd595147b940f96520400100000000000000000044c1a26c32e090a6ab3a9fd5bd1f1272a2b4134b930ba20ce8ac5acbd720b3d97353ec676c79021724744431", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 141, + "name": "SECPOOL", + "slug": "secpool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 5601814, + "expectedWeight": 3991928, + "similarity": 0.9537877497871488 + } + }, + { + "id": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "height": 890447, + "version": 568860672, + "timestamp": 1743541240, + "bits": 386038124, + "nonce": 4008077709, + "difficulty": 113757508810854, + "merkle_root": "8c3b098e4e50b67075a4fc52bf4cd603aaa450c240c18a865c9ddc0f27104f5f", + "tx_count": 1919, + "size": 1747789, + "weight": 3993172, + "previousblockhash": "00000000000000000001fe3d26b02146d0fc2192db06cbc12478d4b347f5306b", + "mediantime": 1743536834, + "stale": false, + "extras": { + "reward": 314435106, + "coinbaseRaw": "034f960d0f2f736c7573682f65000002fba05ef1fabe6d6df8d29032ea6f9ab1debd223651f30887df779c6195869e70a7b787b3a15f4b1710000000000000000000ee5f0c00b20200000000", + "orphans": [], + "medianFee": 1.4653828213500366, + "feeRange": [ + 1.0845070422535212, + 1.2, + 1.51, + 2.0141129032258065, + 2.3893805309734515, + 4.025477707006369, + 300.0065359477124 + ], + "totalFees": 1935106, + "avgFee": 1008, + "avgFeeRate": 1, + "utxoSetChange": -4244, + "avgTxSize": 910.58, + "totalInputs": 9909, + "totalOutputs": 5665, + "totalOutputAmt": 210763861504, + "segwitTotalTxs": 1720, + "segwitTotalSize": 1629450, + "segwitTotalWeight": 3519924, + "feePercentiles": null, + "virtualSize": 998293, + "coinbaseAddress": "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11", + "coinbaseAddresses": [ + "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 1f0cbbec8bc4c945e4e16249b11eee911eded55f OP_EQUAL", + "coinbaseSignatureAscii": "\u0003O–\r\u000f/slush/e\u0000\u0000\u0002û ^ñú¾mmøÒ2êoš±Þ½\"6Qó\b‡ßwœa•†žp§·‡³¡_K\u0017\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000î_\f\u0000²\u0002\u0000\u0000\u0000\u0000", + "header": "0020e8216b30f547b3d47824c1cb06db9221fcd04621b0263dfe010000000000000000005f4f10270fdc9d5c868ac140c250a4aa03d64cbf52fca47570b6504e8e093b8cf853ec676c7902178d69e6ee", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 43, + "name": "Braiins Pool", + "slug": "braiinspool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 2059571, + "expectedWeight": 3991720, + "similarity": 0.9149852183486826 + } + } + ], + "mempool-blocks": [ + { + "blockSize": 1779311, + "blockVSize": 997968.5, + "nTx": 2132, + "totalFees": 2902870, + "medianFee": 2.0479263387949875, + "feeRange": [ + 1.0721153846153846, + 1.9980563654033041, + 2.2195704057279237, + 3.009493670886076, + 3.4955223880597015, + 6.0246913580246915, + 218.1818181818182 + ] + }, + { + "blockSize": 1959636, + "blockVSize": 997903.5, + "nTx": 497, + "totalFees": 1093076, + "medianFee": 1.102049424602265, + "feeRange": [ + 1.0401794819498267, + 1.0548148148148149, + 1.0548148148148149, + 1.0548148148148149, + 1.0548148148148149, + 1.0761096766260911, + 1.1021605957228275 + ] + }, + { + "blockSize": 1477260, + "blockVSize": 997997.25, + "nTx": 720, + "totalFees": 1016195, + "medianFee": 1.007409072434199, + "feeRange": [ + 1, + 1.0019120458891013, + 1.0040863981319323, + 1.0081019768823594, + 1.018450184501845, + 1.0203327171903882, + 1.0485018498190837 + ] + }, + { + "blockSize": 1021308, + "blockVSize": 431071.5, + "nTx": 823, + "totalFees": 432342, + "medianFee": 0, + "feeRange": [ + 1, + 1, + 1, + 1.0028011204481793, + 1.0042075736325387, + 1.0053475935828877, + 1.0068649885583525 + ] + } + ] +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx01_ws_mempool_blocks_01.json b/frontend/cypress/fixtures/details_rbf/tx01_ws_mempool_blocks_01.json new file mode 100644 index 000000000..47a685757 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx01_ws_mempool_blocks_01.json @@ -0,0 +1,68 @@ +{ + "mempool-blocks": [ + { + "blockSize": 1780038, + "blockVSize": 997989.75, + "nTx": 2134, + "totalFees": 2919589, + "medianFee": 2.0479263387949875, + "feeRange": [ + 1.0101010101010102, + 2, + 2.235576923076923, + 3.010452961672474, + 3.5240274599542336, + 6.032085561497326, + 218.1818181818182 + ] + }, + { + "blockSize": 1958446, + "blockVSize": 997996, + "nTx": 503, + "totalFees": 1093277, + "medianFee": 1.102049424602265, + "feeRange": [ + 1.0101010101010102, + 1.0548148148148149, + 1.0548148148148149, + 1.0548148148148149, + 1.067677314564158, + 1.0761096766260911, + 1.1021605957228275 + ] + }, + { + "blockSize": 1477611, + "blockVSize": 997927.5, + "nTx": 725, + "totalFees": 1016311, + "medianFee": 1.0075971559364956, + "feeRange": [ + 1, + 1.0019334049409236, + 1.0042075736325387, + 1.0081019768823594, + 1.018450184501845, + 1.0203327171903882, + 1.0548148148148149 + ] + }, + { + "blockSize": 1028219, + "blockVSize": 435137, + "nTx": 833, + "totalFees": 436414, + "medianFee": 0, + "feeRange": [ + 1, + 1, + 1, + 1.0028011204481793, + 1.004231311706629, + 1.0053475935828877, + 1.0068649885583525 + ] + } + ] +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx01_ws_stratum_jobs.json b/frontend/cypress/fixtures/details_rbf/tx01_ws_stratum_jobs.json new file mode 100644 index 000000000..48d2fe01e --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx01_ws_stratum_jobs.json @@ -0,0 +1,1235 @@ +{ + "stratumJobs": { + "2": { + "jobId": "1743541734_2517178", + "extraNonce": "00004237", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff510350960d142f756c74696d75732f373833e5005202fe934b78fabe6d6d4bec519f2060f58bc5ba10289fbedb1f9456cc864a15f153cbfed624324382181000000000000000", + "coinbase2": "ffffffff05220200000000000017a914332c82217820c32fb9c107b67356cfdc8f8da6a6876271cc120000000017a91472c52c9c71c5f644cf6e712661710503e65e69ad870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f524501ebbaf365b0d5fa072e2b2429db23696291f2c0383d81f61600af32dd72a5ac21aaf1274a3862af6d00000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "15992cde4826516b7dda391bcec363f8da4f6f335199ec76550b535d667c6bcc", + "4703e3571e20a0fa02766ca895c2043bd1c16364b4163a5d7fe3b669fa47b371", + "1d3822d7192f4c8978ca54d5e3b460ab420907199ba2d751ca682b76b9d8e030", + "55b47a1a7ab09d05ea95e4c36b2ece4f441f1787b6548656a67c93850bad28ca", + "1a231983cfb2837a358b85d7bba94851eeedb3f1af915a67165c49ca36c4ea42", + "1ef6e6152101ed2948ef74ebd6d62959f6b21e5c8c404def4faf88976a999c12", + "d28d9eb88badf7d180885592f79da466d0db6dbfd50b440d1c85a2c9ff1b8f01", + "eddef6dd40e0ed35c83129b484f4848021d781cc05da764df8bac3bc82745d12", + "b62ac40bb764914198729c39526751adc6bb047d3f0990ca742c863028a201da", + "7ec1b0edb5aef0b4a71ebf222d22220eb2c38b9fb07603485c9503b65ff43440", + "b4c149bc9f2f48b6887800706e50c669c86af320759914bcd2a53f7f89b92eda", + "c1cde7654ebfad4609b999082eba464c5a20eb1434e46ccd6d81510a13aa853b" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e6", + "cleanJobs": false, + "received": 1743541734950, + "pool": 2, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff510350960d142f756c74696d75732f373833e5005202fe934b78fabe6d6d4bec519f2060f58bc5ba10289fbedb1f9456cc864a15f153cbfed624324382181000000000000000000042370000000000000000ffffffff05220200000000000017a914332c82217820c32fb9c107b67356cfdc8f8da6a6876271cc120000000017a91472c52c9c71c5f644cf6e712661710503e65e69ad870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f524501ebbaf365b0d5fa072e2b2429db23696291f2c0383d81f61600af32dd72a5ac21aaf1274a3862af6d00000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541734, + "reward": 315388804, + "scriptsig": "0350960d142f756c74696d75732f373833e5005202fe934b78fabe6d6d4bec519f2060f58bc5ba10289fbedb1f9456cc864a15f153cbfed624324382181000000000000000000042370000000000000000" + }, + "4": { + "jobId": "450915", + "extraNonce": "00", + "extraNonce2Size": 7, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff580350960d1b4d696e6564206279204c75786f72205465636868005702fe933c3bfabe6d6d8ae1255df7029c6f68cd8ea446d227ef3dc39ef1ac7a55085969307c7bfaa736100000000000000000005813", + "coinbase2": "ffffffff05220200000000000017a914bf73ad4cf3a107812bad3deb310611bee49a3c79875173cc120000000017a914056adde53ebc396a1b3b678bb0d3a5c116ff430c870000000000000000266a24aa21a9ed0844cc9ee7cc307219c6002e18930cdde972a552bc8d1eabad7976208a0e1b8000000000000000002f6a2d434f524501a37cf4faa0758b26dca666f3e36d42fa15cc0106f459cc4ca322d298304ff163b2a360d756c5db8400000000000000002b6a2952534b424c4f434b3a1357f52232e7585b34c3e0cd5d433a50aac01937aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "71b686070fe7bdfaf6a8812ab487fd91d15e21c90fcf5d6bafca745e9b474a19", + "802911f2f657223b6cf06eec9b523b005b6bc2bec56b8fb35d777ef275398ba7", + "7a2626e535df0e88542c11e2c939de4d49e29a654da437bdbbf9d4b89ad8cea1", + "c49488289a1335c2bcf24b8661ce01ace0e6c35e0105a116fb55ccf981e46c59", + "8f8908b51423dc8ea13a97524f4159d629754f89c8ddfab140b8c695442215f3", + "48cc791e2e2fd0d16c9bf774967162a333ee2eeeedbace83f9ac5368bbc1dc97", + "8e25b97a71a9121cbec116aae71825cc1f8679687c44ef7dd94d2841244e16d9", + "e5328ff6cc72161b5c5e97152a4544f3900ac0a63abde9564919ffa30e28505d", + "3f12570807f882825eaa97b598308bd27e7f36d8ba79e039d81dcfe6cbf1fdd6", + "6afcf4b7a3fd41e8b6290459722bf83f0aff5a36b2b54e9f4210f9cb8bef51b9", + "1a9bdc30e04b9705106827938046b8485384b584254c2f93afdc50ed42a189b2" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e6", + "cleanJobs": false, + "received": 1743541735146, + "pool": 4, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff580350960d1b4d696e6564206279204c75786f72205465636868005702fe933c3bfabe6d6d8ae1255df7029c6f68cd8ea446d227ef3dc39ef1ac7a55085969307c7bfaa7361000000000000000000058130000000000000000ffffffff05220200000000000017a914bf73ad4cf3a107812bad3deb310611bee49a3c79875173cc120000000017a914056adde53ebc396a1b3b678bb0d3a5c116ff430c870000000000000000266a24aa21a9ed0844cc9ee7cc307219c6002e18930cdde972a552bc8d1eabad7976208a0e1b8000000000000000002f6a2d434f524501a37cf4faa0758b26dca666f3e36d42fa15cc0106f459cc4ca322d298304ff163b2a360d756c5db8400000000000000002b6a2952534b424c4f434b3a1357f52232e7585b34c3e0cd5d433a50aac01937aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541734, + "reward": 315389299, + "scriptsig": "0350960d1b4d696e6564206279204c75786f72205465636868005702fe933c3bfabe6d6d8ae1255df7029c6f68cd8ea446d227ef3dc39ef1ac7a55085969307c7bfaa7361000000000000000000058130000000000000000" + }, + "36": { + "jobId": "B75cp1aWq", + "extraNonce": "00", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff640350960d2cfabe6d6da35ece7974edab96759db21f0953f4eaa2200241771a0976d18c5c6e27c5340a10000000f09f909f092f4632506f6f6c2f6b000000000000000000000000000000000000000000000000000000000000000000000005", + "coinbase2": "0722020000000000001976a914c6740a12d0a7d556f89782bf5faf0e12cf25a63988ac3a4acb12000000001976a91469f2a01f4ff9e6ac24df9062e9828753474b348088ac0000000000000000266a24aa21a9ed534cb114f7fd20c22a47d45e6bb6c5460bf3a9cce404265e0961cfd6c881190700000000000000002f6a2d434f5245017c706ca44a28fdd25761250961276bd61d5aa87be7ec323813c943336c579e238228a8ebd096a7e50000000000000000126a10455853415401051b0f0e0e0b1f1200130000000000000000266a2448617468241c617362765014f47be645944e22747340fba7c5a705548dbae9b4a84e62ff00000000000000002c6a4c2952534b424c4f434b3a81cc66e44e74bd510bfde3b83b286662e6a2e597aa8a53c435370b130070fc91dc608334", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "723847223fbee3db7918859bb384999905cba259070859ec77ebfa329b05065a", + "cd7913bf8e499b13ad83c84b2dcc5de216823ff59e45cc76af8f47570114476e", + "2cec318ccfb9046dd1b929e52e7d131cfbf0499fa0f17470944bd0a39d7392a7", + "98531b18c2ce0bfd02dbc7efc9463a4a891cb30c3dda244ebc17d34a52e6e2b9", + "66a73af7f957129069b86ec454ab2e8ac4399e2f6549d6e0e4367844abc8d546", + "6c30f7579f6b6734d6ce96b86e33d318513bafeee287870a960c5a7081244a0c", + "aac144dd768fb69fb813f0eebdd02afb8b55e00d0a61101cf167ecda07a1bc2b", + "ed81f05edaf48584c328a7d4c1f2de3720c2eaba0fa7aa37e4f0792603c60b67", + "98ce77b30480722596a787892493dfdac342b5c717aa5d2a3a4ca1a4b461321c", + "d1372489dd7304c4c44214da63b071c987ee084440861140fb56d5ea03ba8e39", + "efeeb673f474c3d4724bf2503a0903ff73ff01afabc37ffa694f8c79022d7078" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55cb", + "cleanJobs": false, + "received": 1743541719279, + "pool": 36, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff640350960d2cfabe6d6da35ece7974edab96759db21f0953f4eaa2200241771a0976d18c5c6e27c5340a10000000f09f909f092f4632506f6f6c2f6b0000000000000000000000000000000000000000000000000000000000000000000000050000000000000000000722020000000000001976a914c6740a12d0a7d556f89782bf5faf0e12cf25a63988ac3a4acb12000000001976a91469f2a01f4ff9e6ac24df9062e9828753474b348088ac0000000000000000266a24aa21a9ed534cb114f7fd20c22a47d45e6bb6c5460bf3a9cce404265e0961cfd6c881190700000000000000002f6a2d434f5245017c706ca44a28fdd25761250961276bd61d5aa87be7ec323813c943336c579e238228a8ebd096a7e50000000000000000126a10455853415401051b0f0e0e0b1f1200130000000000000000266a2448617468241c617362765014f47be645944e22747340fba7c5a705548dbae9b4a84e62ff00000000000000002c6a4c2952534b424c4f434b3a81cc66e44e74bd510bfde3b83b286662e6a2e597aa8a53c435370b130070fc91dc608334", + "height": 890448, + "timestamp": 1743541707, + "reward": 315313244, + "scriptsig": "0350960d2cfabe6d6da35ece7974edab96759db21f0953f4eaa2200241771a0976d18c5c6e27c5340a10000000f09f909f092f4632506f6f6c2f6b0000000000000000000000000000000000000000000000000000000000000000000000050000000000" + }, + "43": { + "jobId": "131d", + "extraNonce": "00", + "extraNonce2Size": 6, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4c0350960d0f2f736c7573682f65000303fe9334c4fabe6d6dbb9c929fe497398b9a628aa161b72ed26b10e8ca5f29c485f42b468355043a15100000000000000000004e802e", + "coinbase2": "ffffffff038473cc120000000017a9141f0cbbec8bc4c945e4e16249b11eee911eded55f870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "15992cde4826516b7dda391bcec363f8da4f6f335199ec76550b535d667c6bcc", + "4703e3571e20a0fa02766ca895c2043bd1c16364b4163a5d7fe3b669fa47b371", + "1d3822d7192f4c8978ca54d5e3b460ab420907199ba2d751ca682b76b9d8e030", + "55b47a1a7ab09d05ea95e4c36b2ece4f441f1787b6548656a67c93850bad28ca", + "1a231983cfb2837a358b85d7bba94851eeedb3f1af915a67165c49ca36c4ea42", + "1ef6e6152101ed2948ef74ebd6d62959f6b21e5c8c404def4faf88976a999c12", + "d28d9eb88badf7d180885592f79da466d0db6dbfd50b440d1c85a2c9ff1b8f01", + "eddef6dd40e0ed35c83129b484f4848021d781cc05da764df8bac3bc82745d12", + "b62ac40bb764914198729c39526751adc6bb047d3f0990ca742c863028a201da", + "7ec1b0edb5aef0b4a71ebf222d22220eb2c38b9fb07603485c9503b65ff43440", + "b4c149bc9f2f48b6887800706e50c669c86af320759914bcd2a53f7f89b92eda", + "c1cde7654ebfad4609b999082eba464c5a20eb1434e46ccd6d81510a13aa853b" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e6", + "cleanJobs": false, + "received": 1743541734908, + "pool": 43, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4c0350960d0f2f736c7573682f65000303fe9334c4fabe6d6dbb9c929fe497398b9a628aa161b72ed26b10e8ca5f29c485f42b468355043a15100000000000000000004e802e00000000000000ffffffff038473cc120000000017a9141f0cbbec8bc4c945e4e16249b11eee911eded55f870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541734, + "reward": 315388804, + "scriptsig": "0350960d0f2f736c7573682f65000303fe9334c4fabe6d6dbb9c929fe497398b9a628aa161b72ed26b10e8ca5f29c485f42b468355043a15100000000000000000004e802e00000000000000" + }, + "44": { + "jobId": "3750742", + "extraNonce": "00002c10", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff580350960d1b4d696e656420627920416e74506f6f6c3830369c001700fe93b694fabe6d6d433177d6fe558d8cee3035bb1b481156f8b411c21bddef7fb8aec740e4617c501000000000000000", + "coinbase2": "ffffffff06220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade7876271cc120000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d54e3ecda72cb7961caa4b541b1e322bcfe0b5a0300000000000000000146a12455853415401000d130f0e0e0b041f12001300000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "15992cde4826516b7dda391bcec363f8da4f6f335199ec76550b535d667c6bcc", + "4703e3571e20a0fa02766ca895c2043bd1c16364b4163a5d7fe3b669fa47b371", + "1d3822d7192f4c8978ca54d5e3b460ab420907199ba2d751ca682b76b9d8e030", + "55b47a1a7ab09d05ea95e4c36b2ece4f441f1787b6548656a67c93850bad28ca", + "1a231983cfb2837a358b85d7bba94851eeedb3f1af915a67165c49ca36c4ea42", + "1ef6e6152101ed2948ef74ebd6d62959f6b21e5c8c404def4faf88976a999c12", + "d28d9eb88badf7d180885592f79da466d0db6dbfd50b440d1c85a2c9ff1b8f01", + "eddef6dd40e0ed35c83129b484f4848021d781cc05da764df8bac3bc82745d12", + "b62ac40bb764914198729c39526751adc6bb047d3f0990ca742c863028a201da", + "7ec1b0edb5aef0b4a71ebf222d22220eb2c38b9fb07603485c9503b65ff43440", + "b4c149bc9f2f48b6887800706e50c669c86af320759914bcd2a53f7f89b92eda", + "c1cde7654ebfad4609b999082eba464c5a20eb1434e46ccd6d81510a13aa853b" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e7", + "cleanJobs": false, + "received": 1743541735750, + "pool": 44, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff580350960d1b4d696e656420627920416e74506f6f6c3830369c001700fe93b694fabe6d6d433177d6fe558d8cee3035bb1b481156f8b411c21bddef7fb8aec740e4617c50100000000000000000002c100000000000000000ffffffff06220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade7876271cc120000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d54e3ecda72cb7961caa4b541b1e322bcfe0b5a0300000000000000000146a12455853415401000d130f0e0e0b041f12001300000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541735, + "reward": 315388804, + "scriptsig": "0350960d1b4d696e656420627920416e74506f6f6c3830369c001700fe93b694fabe6d6d433177d6fe558d8cee3035bb1b481156f8b411c21bddef7fb8aec740e4617c50100000000000000000002c100000000000000000" + }, + "49": { + "jobId": "67444bb00005e280", + "extraNonce": "1eecec72", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff350350960d0004d155ec67047a5741090c", + "coinbase2": "0a636b706f6f6c112f736f6c6f2e636b706f6f6c2e6f72672fffffffff03dd276b12000000001976a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac483a60000000000016001451ed61d2f6aa260cc72cdf743e4e436a82c010270000000000000000266a24aa21a9ed6c1f16b33ae5c1c628b53c6011f38866955c8b59448e5aee14f9967c8cb7ab1c00000000", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "723847223fbee3db7918859bb384999905cba259070859ec77ebfa329b05065a", + "cd7913bf8e499b13ad83c84b2dcc5de216823ff59e45cc76af8f47570114476e", + "2cec318ccfb9046dd1b929e52e7d131cfbf0499fa0f17470944bd0a39d7392a7", + "98531b18c2ce0bfd02dbc7efc9463a4a891cb30c3dda244ebc17d34a52e6e2b9", + "66a73af7f957129069b86ec454ab2e8ac4399e2f6549d6e0e4367844abc8d546", + "6c30f7579f6b6734d6ce96b86e33d318513bafeee287870a960c5a7081244a0c", + "717d5dac15b3253aeec8e1079141760337bebe0101d0b4e4c43384416ca132ad", + "9eba7f7def1ad83bb3d7df151f880fa76c4b4ec3b531a4e0856a1e7437d3d060", + "0558088354e8bf625f19e28e6ec02231d61a31c26f215d2e1253baf0452064bb", + "6c25959fb0244719945408c5da48055f0d9ad30240ebc520c2d81b5df1b0f3d7", + "150163eb46086007fafbef5c2c71faf3f8b4690c338e2d74a966ff2650df63c3" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55d1", + "cleanJobs": false, + "received": 1743541713511, + "pool": 49, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff350350960d0004d155ec67047a5741090c1eecec7200000000000000000a636b706f6f6c112f736f6c6f2e636b706f6f6c2e6f72672fffffffff03dd276b12000000001976a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac483a60000000000016001451ed61d2f6aa260cc72cdf743e4e436a82c010270000000000000000266a24aa21a9ed6c1f16b33ae5c1c628b53c6011f38866955c8b59448e5aee14f9967c8cb7ab1c00000000", + "height": 890448, + "timestamp": 1743541713, + "reward": 315318821, + "scriptsig": "0350960d0004d155ec67047a5741090c1eecec7200000000000000000a636b706f6f6c112f736f6c6f2e636b706f6f6c2e6f72672f" + }, + "73": { + "jobId": "8711", + "extraNonce": "41fade6a", + "extraNonce2Size": 4, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff610350960d1e2f5669614254432f4d696e6564206279206d6f6e6f6e6175746963616c2f2cfabe6d6d0d586d106170cc5c7380e81f3e79c40726b2313bdb6263eedba507d9cdc53bca1000000000000000101187c609caa65f88", + "coinbase2": "ffffffff040388cb12000000001976a914fb37342f6275b13936799def06f2eb4c0f20151588ac00000000000000002b6a2952534b424c4f434b3a280a7381d0ccac30cd6dcfd0beae78f22cb58d6caa8a53c435370b120070fc920000000000000000146a124558534154011508000113021b1a1f1200130000000000000000266a24aa21a9ed10c31ffb387781fdab75b9b2e4994223622049c78f1f81d05a4a421ae178626000000000", + "merkleBranches": [ + "b4efa4f5b9d41f9fc63480038ba7586dac490d08fd738aaa691d49bcbee1dd54", + "97c694c3a659a43a7ed8f7e2ae4d06927a152b348f33e7a51c1b5dd242631e72", + "09350f4bc26ad644fadeec9bea500daadb35bf43f8f39d82a8adb708661a8f18", + "e5f3663e21ccc3f4565529a73b11811dd68abcc9ac701aa494b7b18782eefa23", + "bcdfb143b322e8677b8edb90417a1ec739a6d9eb8efc84e5ba6424e7f3adfc0f", + "b90794158874321306985105e45aa872a9646a5bf683a070d7ca6602450ad8e5", + "26e4fb36ec030a6510c9047120575dca2a8b8760674af4284f5c27e53f03f46b", + "e6a15e522b650f9cebfdd003c0d29f5ca571bf704d63b9aa627ce915acb229f7", + "392d3e593f80e9180a3bce84ebaa044d5406cad39897c78e58e66ca59e7499c8", + "ce8409542b60ed5ca9c567b89fa9a7c2e5e8e2bbf2b42b0ce8edb0836a7a2e82", + "0e8201e0367364ed40263747547d3a0c26926d2b94799b414c22a25c6fc04d43", + "33e95cd9da59d9e33e239b9b4e647a2da98f36ef6a137f40ba428782bb1c4021" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55d7", + "cleanJobs": false, + "received": 1743541719896, + "pool": 73, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff610350960d1e2f5669614254432f4d696e6564206279206d6f6e6f6e6175746963616c2f2cfabe6d6d0d586d106170cc5c7380e81f3e79c40726b2313bdb6263eedba507d9cdc53bca1000000000000000101187c609caa65f8841fade6a00000000ffffffff040388cb12000000001976a914fb37342f6275b13936799def06f2eb4c0f20151588ac00000000000000002b6a2952534b424c4f434b3a280a7381d0ccac30cd6dcfd0beae78f22cb58d6caa8a53c435370b120070fc920000000000000000146a124558534154011508000113021b1a1f1200130000000000000000266a24aa21a9ed10c31ffb387781fdab75b9b2e4994223622049c78f1f81d05a4a421ae178626000000000", + "height": 890448, + "timestamp": 1743541719, + "reward": 315328515, + "scriptsig": "0350960d1e2f5669614254432f4d696e6564206279206d6f6e6f6e6175746963616c2f2cfabe6d6d0d586d106170cc5c7380e81f3e79c40726b2313bdb6263eedba507d9cdc53bca1000000000000000101187c609caa65f8841fade6a00000000" + }, + "94": { + "jobId": "1186343", + "extraNonce": "0000776f", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff570350960d1a2f706f6f6c696e2e636f6d2f66702f3936369c01b704fe93ba97fabe6d6d89255a8f9c17d3a3dc753a3e2b2eb55a116c1d7280bcf4b4f76a0af1f2e12ce31000000000000000", + "coinbase2": "ffffffff038473cc120000000017a9141366dca425687186b9b54e27e4ed48163b5beb80870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "15992cde4826516b7dda391bcec363f8da4f6f335199ec76550b535d667c6bcc", + "4703e3571e20a0fa02766ca895c2043bd1c16364b4163a5d7fe3b669fa47b371", + "1d3822d7192f4c8978ca54d5e3b460ab420907199ba2d751ca682b76b9d8e030", + "55b47a1a7ab09d05ea95e4c36b2ece4f441f1787b6548656a67c93850bad28ca", + "1a231983cfb2837a358b85d7bba94851eeedb3f1af915a67165c49ca36c4ea42", + "1ef6e6152101ed2948ef74ebd6d62959f6b21e5c8c404def4faf88976a999c12", + "d28d9eb88badf7d180885592f79da466d0db6dbfd50b440d1c85a2c9ff1b8f01", + "eddef6dd40e0ed35c83129b484f4848021d781cc05da764df8bac3bc82745d12", + "b62ac40bb764914198729c39526751adc6bb047d3f0990ca742c863028a201da", + "7ec1b0edb5aef0b4a71ebf222d22220eb2c38b9fb07603485c9503b65ff43440", + "b4c149bc9f2f48b6887800706e50c669c86af320759914bcd2a53f7f89b92eda", + "c1cde7654ebfad4609b999082eba464c5a20eb1434e46ccd6d81510a13aa853b" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e7", + "cleanJobs": false, + "received": 1743541735127, + "pool": 94, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff570350960d1a2f706f6f6c696e2e636f6d2f66702f3936369c01b704fe93ba97fabe6d6d89255a8f9c17d3a3dc753a3e2b2eb55a116c1d7280bcf4b4f76a0af1f2e12ce310000000000000000000776f0000000000000000ffffffff038473cc120000000017a9141366dca425687186b9b54e27e4ed48163b5beb80870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541735, + "reward": 315388804, + "scriptsig": "0350960d1a2f706f6f6c696e2e636f6d2f66702f3936369c01b704fe93ba97fabe6d6d89255a8f9c17d3a3dc753a3e2b2eb55a116c1d7280bcf4b4f76a0af1f2e12ce310000000000000000000776f0000000000000000" + }, + "102": { + "jobId": "33", + "extraNonce": "04264b21", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff570350960d04d655ec67537069646572506f6f6c2f7573383838382ffabe6d6d013135a66fcdf604440c12e28ffb6f448d1d7b633d16737217081646c41912270100000000000000b286b3fc", + "coinbase2": "ffffffff04b55dcb12000000001976a914717a4c9074577a05af94271c32b249d298a22d9888ac0000000000000000266a24aa21a9eded8df9e053ff66fe09268a9df4e369d60a48437a012a1b157e143bb967379cab00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d596a6689031f48a857d344e1a42fdb272bb15d6210000000000000000126a10455853415401120f080304111f12001300000000", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "723847223fbee3db7918859bb384999905cba259070859ec77ebfa329b05065a", + "cd7913bf8e499b13ad83c84b2dcc5de216823ff59e45cc76af8f47570114476e", + "2cec318ccfb9046dd1b929e52e7d131cfbf0499fa0f17470944bd0a39d7392a7", + "98531b18c2ce0bfd02dbc7efc9463a4a891cb30c3dda244ebc17d34a52e6e2b9", + "66a73af7f957129069b86ec454ab2e8ac4399e2f6549d6e0e4367844abc8d546", + "6c30f7579f6b6734d6ce96b86e33d318513bafeee287870a960c5a7081244a0c", + "717d5dac15b3253aeec8e1079141760337bebe0101d0b4e4c43384416ca132ad", + "9eba7f7def1ad83bb3d7df151f880fa76c4b4ec3b531a4e0856a1e7437d3d060", + "5fb573157a34ed041274de444f9dd86e210189cf325bdbde44a92edded8f1b90", + "1e77c2e67bf442dd78a2e078fe9b7bb1bece599bf7e77e2086e5d0659afedf9e", + "e6c75e3d1859a1d8d6a10ac5fe990969202fa37d0715c7ca50c2159e3b3a463e" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55ce", + "cleanJobs": false, + "received": 1743541719031, + "pool": 102, + "coinbase": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff570350960d04d655ec67537069646572506f6f6c2f7573383838382ffabe6d6d013135a66fcdf604440c12e28ffb6f448d1d7b633d16737217081646c41912270100000000000000b286b3fc04264b210000000000000000ffffffff04b55dcb12000000001976a914717a4c9074577a05af94271c32b249d298a22d9888ac0000000000000000266a24aa21a9eded8df9e053ff66fe09268a9df4e369d60a48437a012a1b157e143bb967379cab00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d596a6689031f48a857d344e1a42fdb272bb15d6210000000000000000126a10455853415401120f080304111f12001300000000", + "height": 890448, + "timestamp": 1743541710, + "reward": 315317685, + "scriptsig": "0350960d04d655ec67537069646572506f6f6c2f7573383838382ffabe6d6d013135a66fcdf604440c12e28ffb6f448d1d7b633d16737217081646c41912270100000000000000b286b3fc04264b210000000000000000" + }, + "105": { + "jobId": "4287955", + "extraNonce": "0000e9a5", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff500350960d1362696e616e63652f3832319c001606fe93bd58fabe6d6d2cbf0bfed0a1d41f829160053963a92e7a2c44942646e992955ce00fd53f62991000000000000000", + "coinbase2": "ffffffff05220200000000000017a914265ae1340f5d442d099ffc27b443ffdba4bc0346876271cc120000000017a9149e3e8a50d9acb3ac7649625432e2207c25e0faf8870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f5245012d058b58dcf4b0db11168c62d3109f6e02710b02221456a6c24c9891680d295eb6bc57c6fb68e8f100000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "15992cde4826516b7dda391bcec363f8da4f6f335199ec76550b535d667c6bcc", + "4703e3571e20a0fa02766ca895c2043bd1c16364b4163a5d7fe3b669fa47b371", + "1d3822d7192f4c8978ca54d5e3b460ab420907199ba2d751ca682b76b9d8e030", + "55b47a1a7ab09d05ea95e4c36b2ece4f441f1787b6548656a67c93850bad28ca", + "1a231983cfb2837a358b85d7bba94851eeedb3f1af915a67165c49ca36c4ea42", + "1ef6e6152101ed2948ef74ebd6d62959f6b21e5c8c404def4faf88976a999c12", + "d28d9eb88badf7d180885592f79da466d0db6dbfd50b440d1c85a2c9ff1b8f01", + "eddef6dd40e0ed35c83129b484f4848021d781cc05da764df8bac3bc82745d12", + "b62ac40bb764914198729c39526751adc6bb047d3f0990ca742c863028a201da", + "7ec1b0edb5aef0b4a71ebf222d22220eb2c38b9fb07603485c9503b65ff43440", + "b4c149bc9f2f48b6887800706e50c669c86af320759914bcd2a53f7f89b92eda", + "c1cde7654ebfad4609b999082eba464c5a20eb1434e46ccd6d81510a13aa853b" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e7", + "cleanJobs": false, + "received": 1743541735177, + "pool": 105, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff500350960d1362696e616e63652f3832319c001606fe93bd58fabe6d6d2cbf0bfed0a1d41f829160053963a92e7a2c44942646e992955ce00fd53f629910000000000000000000e9a50000000000000000ffffffff05220200000000000017a914265ae1340f5d442d099ffc27b443ffdba4bc0346876271cc120000000017a9149e3e8a50d9acb3ac7649625432e2207c25e0faf8870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f5245012d058b58dcf4b0db11168c62d3109f6e02710b02221456a6c24c9891680d295eb6bc57c6fb68e8f100000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541735, + "reward": 315388804, + "scriptsig": "0350960d1362696e616e63652f3832319c001606fe93bd58fabe6d6d2cbf0bfed0a1d41f829160053963a92e7a2c44942646e992955ce00fd53f629910000000000000000000e9a50000000000000000" + }, + "111": { + "jobId": "7", + "extraNonce": "31a044e7", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff310350960d04e155ec672f466f756e6472792055534120506f6f6c202364726f70676f6c642f", + "coinbase2": "ffffffff0522020000000000002251200f9dab1a72f7c48da8a1df2f913bef649bfc0d77072dffd11329b8048293d7a37f28cc12000000002200207086320071974eef5e72eaa01dd9096e10c0383483855ea6b344259c244f73c20000000000000000266a24aa21a9eda9d49baa733153f684d3e885e731818571f650adfef974d99fe1e11b69af0bc600000000000000002f6a2d434f524501359b5fbc5b294e953dbec5cbb769e2186bb30e56e6d18fda214e5b9f350ffc7b6cf3058b9026e76500000000000000002b6a2952534b424c4f434b3aaadde8fc40282f7f5a2c02a16700e42ad9f6bc5aaa8a53c435370b120070fc9200000000", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "470ea829e1e3085254952441894afcb97582e5675fbb35c226edf79a226890d5", + "09a4875a0b37d78e9643154473bb974b2eba5c800e3cd58c99adfb196f383358", + "2cec318ccfb9046dd1b929e52e7d131cfbf0499fa0f17470944bd0a39d7392a7", + "98531b18c2ce0bfd02dbc7efc9463a4a891cb30c3dda244ebc17d34a52e6e2b9", + "66a73af7f957129069b86ec454ab2e8ac4399e2f6549d6e0e4367844abc8d546", + "2af55ecd2164a88f5df8c3de727633690dabb2de4b61827ac86adcd927cd3e24", + "8601c35a421ac857f22a1ea05b53f003771aaaaf1e65bea339bdc80a9986e671", + "230d09f9ee198e35e1b1be1de2f87acf1a1e73690d680b85ba96bd21790e8766", + "8f4cee6b0fc9bca8608134c4a93116cf4de4ab753863219201cd20ff06b18d1e", + "4de04f813d0c4fbd5b4fb79e490f76ba4da31391d25f0447f1e9eccc935c9fe4", + "c3de6f84d58eeb5277be653e241331dddd19d056ce00ff7d3d9ecdefadfa9067" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e1", + "cleanJobs": false, + "received": 1743541729815, + "pool": 111, + "coinbase": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff310350960d04e155ec672f466f756e6472792055534120506f6f6c202364726f70676f6c642f31a044e70000000000000000ffffffff0522020000000000002251200f9dab1a72f7c48da8a1df2f913bef649bfc0d77072dffd11329b8048293d7a37f28cc12000000002200207086320071974eef5e72eaa01dd9096e10c0383483855ea6b344259c244f73c20000000000000000266a24aa21a9eda9d49baa733153f684d3e885e731818571f650adfef974d99fe1e11b69af0bc600000000000000002f6a2d434f524501359b5fbc5b294e953dbec5cbb769e2186bb30e56e6d18fda214e5b9f350ffc7b6cf3058b9026e76500000000000000002b6a2952534b424c4f434b3aaadde8fc40282f7f5a2c02a16700e42ad9f6bc5aaa8a53c435370b120070fc9200000000", + "height": 890448, + "timestamp": 1743541729, + "reward": 315370145, + "scriptsig": "0350960d04e155ec672f466f756e6472792055534120506f6f6c202364726f70676f6c642f31a044e70000000000000000" + }, + "112": { + "jobId": "12", + "extraNonce": "050300c0", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff290350960d04db55ec672f53424943727970746f2e636f6d20506f6f6c2f", + "coinbase2": "ffffffff02ffc8cb1200000000160014cab30e9b367d646f326ace7fcaf3c9ce4afc37530000000000000000266a24aa21a9edd0dd8d14ec5a97c6fd3de15f02c86d889e932e0262807795fa7c127e5a8fa0be00000000", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "b41e3afa7672a36d6a462612f3bdc9ee8000388471b29ba1d187135b435f3efa", + "4236249ae2883af9d68c5570f893aa808a2a15d61ea654444cebfb9b3ba2c598", + "2cec318ccfb9046dd1b929e52e7d131cfbf0499fa0f17470944bd0a39d7392a7", + "98531b18c2ce0bfd02dbc7efc9463a4a891cb30c3dda244ebc17d34a52e6e2b9", + "66a73af7f957129069b86ec454ab2e8ac4399e2f6549d6e0e4367844abc8d546", + "4b5b7ee5fe7813f0e1ae5d1265215062d449d5c50b5edede2816274216e88a8d", + "6624f27663e2330c010fe68f5f397ebef70a1f6188ccb104e9eff97a9e48bb02", + "b94d8d0e4cc4b8b62bd4f4d70fc4497f85b05280b43e0b5dc399cc8778a1074a", + "a97d374c628f07cc5283b577558c3c0e3f2f3c8dac8df6ccdea7cf26b6776fa5", + "190f5f1bc8535d731fd095dd8e97ec3c33facdbce8614ca654017f3baf84a3fe", + "102b7769fe8b53ca163151eb461c767d331c465d95056f556e38a07298e6046a" + ], + "version": "20000004", + "bits": "1702796c", + "time": "67ec55db", + "cleanJobs": false, + "received": 1743541728640, + "pool": 112, + "coinbase": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff290350960d04db55ec672f53424943727970746f2e636f6d20506f6f6c2f050300c00000000000000000ffffffff02ffc8cb1200000000160014cab30e9b367d646f326ace7fcaf3c9ce4afc37530000000000000000266a24aa21a9edd0dd8d14ec5a97c6fd3de15f02c86d889e932e0262807795fa7c127e5a8fa0be00000000", + "height": 890448, + "timestamp": 1743541723, + "reward": 315345151, + "scriptsig": "0350960d04db55ec672f53424943727970746f2e636f6d20506f6f6c2f050300c00000000000000000" + }, + "141": { + "jobId": "1832214", + "extraNonce": "0000ac40", + "extraNonce2Size": 4, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff510350960d184d696e656420627920536563506f6f6c58007904fe939454fabe6d6d2e07daf9ae89d5bfcdb87dd64455e7357ca9fb3708a2cf08ca8f30a38142a68e1000000000000000", + "coinbase2": "ffffffff05220200000000000017a9148ee90177614ecde53314fd67c46162f315852a07875173cc120000000017a9146582f2551e2a47e1ae8b03fb666401ed7c4552ef870000000000000000266a24aa21a9ed0844cc9ee7cc307219c6002e18930cdde972a552bc8d1eabad7976208a0e1b8000000000000000002f6a2d434f524501a21cbd3caa4fe89bccd1d716c92ce4533e4d4733942c01262fa046fdb8ba0dfce3753405c74aed0e00000000000000002b6a2952534b424c4f434b3a0374e206447243d32da96205942c62a298fddbd7aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "71b686070fe7bdfaf6a8812ab487fd91d15e21c90fcf5d6bafca745e9b474a19", + "802911f2f657223b6cf06eec9b523b005b6bc2bec56b8fb35d777ef275398ba7", + "7a2626e535df0e88542c11e2c939de4d49e29a654da437bdbbf9d4b89ad8cea1", + "c49488289a1335c2bcf24b8661ce01ace0e6c35e0105a116fb55ccf981e46c59", + "8f8908b51423dc8ea13a97524f4159d629754f89c8ddfab140b8c695442215f3", + "48cc791e2e2fd0d16c9bf774967162a333ee2eeeedbace83f9ac5368bbc1dc97", + "8e25b97a71a9121cbec116aae71825cc1f8679687c44ef7dd94d2841244e16d9", + "e5328ff6cc72161b5c5e97152a4544f3900ac0a63abde9564919ffa30e28505d", + "3f12570807f882825eaa97b598308bd27e7f36d8ba79e039d81dcfe6cbf1fdd6", + "6afcf4b7a3fd41e8b6290459722bf83f0aff5a36b2b54e9f4210f9cb8bef51b9", + "1a9bdc30e04b9705106827938046b8485384b584254c2f93afdc50ed42a189b2" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e6", + "cleanJobs": false, + "received": 1743541735015, + "pool": 141, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff510350960d184d696e656420627920536563506f6f6c58007904fe939454fabe6d6d2e07daf9ae89d5bfcdb87dd64455e7357ca9fb3708a2cf08ca8f30a38142a68e10000000000000000000ac4000000000ffffffff05220200000000000017a9148ee90177614ecde53314fd67c46162f315852a07875173cc120000000017a9146582f2551e2a47e1ae8b03fb666401ed7c4552ef870000000000000000266a24aa21a9ed0844cc9ee7cc307219c6002e18930cdde972a552bc8d1eabad7976208a0e1b8000000000000000002f6a2d434f524501a21cbd3caa4fe89bccd1d716c92ce4533e4d4733942c01262fa046fdb8ba0dfce3753405c74aed0e00000000000000002b6a2952534b424c4f434b3a0374e206447243d32da96205942c62a298fddbd7aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541734, + "reward": 315389299, + "scriptsig": "0350960d184d696e656420627920536563506f6f6c58007904fe939454fabe6d6d2e07daf9ae89d5bfcdb87dd64455e7357ca9fb3708a2cf08ca8f30a38142a68e10000000000000000000ac4000000000" + }, + "142": { + "jobId": "67ec55c62cc0f502", + "extraNonce": "b10cf017", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0350960d163c204f4345414e2e58595a203e0f0f30304f43423400024087ffffffff140000000000000000166a144f4342313fd6fdce13000000290000002c029c30562b26000000000016001472fb714ecb210311655a3da22729ff8626bdea590000000000000000106a0e1a27", + "coinbase2": "f65ccf020000000017a9142b636a84e5b12177cd49c687b5a1ebd07fb83fbf8783c76e0200000000220020ac11a29a5721e9602e396acd7e5216c9196b7dbb14cf573921c46e6fea38ac09d6632e020000000017a91449b58fb476449dab3a2e175fe2c1f0d753a33e92878b8ad9010000000017a914cc888b5846746a5a7f449eea90082feaa09b9bf387833cbe01000000001600146983f7fe983e96dc5b43677fb14822a075596364b136ae0100000000220020dc406857cac1b9d3f53c6986acb78e7774c604281076d10d3fc8e873e18df6d0663e6e0100000000160014d192719c5be868068f023b9383b3cbecb6afbb1639205300000000001976a91412c00ad1271bd4c91e4f8ff11e738c285181399d88ac49844e000000000017a9141ef31e9bc9ccb532b8c01950727990cca190656a877aa148000000000016001404afd9f6e36ba7cadb62133c458a5318f41a33de731b42000000000016001415c0899a887e36f6620619b621855d40b41cbf838b5a3b0000000000220020e65791d3c340710f7aebf117d1f38e1c5383c1df0345a2082e8ebff945e9994646d83a00000000001600140345e587cb42c201c30e505ea9f6f5fb935876ac5df731000000000017a914278dadd16d1314122e4dde67ff9152a61c3a9f4787254f3000000000001600146d095474bec41ccc8ad40f94c74dd25b79b83579e85b7e020000000017a914413b5901fe4e591c95405fd446b5b002da575bf0870000000000000000266a24aa21a9ed2a3f54b40a557c2884545b770a050154a46b5dc132eec3604f01714f413b32a300000000", + "merkleBranches": [ + "2b797d78bf7de288eccd666e7b81bf04bc96beb0672b6ece12a8e66c8ab3e989", + "81890975bdb859a5c5527a40a7354d0d60b133fa4eaef69f1738ece447714e2c", + "a7fe518c62a02f13cc9e18d4622af75b5fcd4ebad10f129ea0e9eee165cae6b5", + "626773fea3f749684748ec54e167fceb58e3520cbdcf7926717ab7d3b481a2f1", + "de27b0f244aeed874d5b8e03730e7c386905016563058919a476a7efa0414921", + "52903b24bb6d22b7df633c6a426d014cab6c592069043d51ec4b9a910bb98c3e", + "0834ec8e29e9cea5ff2b05cea6f5ee6ce71088569a44fac069c94f777366e806", + "f792f333b3687ca868e80449265720091bcc29a8bd986b5e5d35a297986f9ea2", + "6ea6eca3a29cf603146043e1e5d6bb20e0460dbc72157bedfe585bf38c7e6c56", + "36fe42124e6a5e5250af65cc12e6d7b6370793e559b5fc76b7a46760a83396d1", + "f1c2b5b11af512e743b9c9f219192dc82b8826760b4678683ea16303e2b65508" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55c5", + "cleanJobs": false, + "received": 1743541714701, + "pool": 142, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0350960d163c204f4345414e2e58595a203e0f0f30304f43423400024087ffffffff140000000000000000166a144f4342313fd6fdce13000000290000002c029c30562b26000000000016001472fb714ecb210311655a3da22729ff8626bdea590000000000000000106a0e1a27b10cf0170000000000000000f65ccf020000000017a9142b636a84e5b12177cd49c687b5a1ebd07fb83fbf8783c76e0200000000220020ac11a29a5721e9602e396acd7e5216c9196b7dbb14cf573921c46e6fea38ac09d6632e020000000017a91449b58fb476449dab3a2e175fe2c1f0d753a33e92878b8ad9010000000017a914cc888b5846746a5a7f449eea90082feaa09b9bf387833cbe01000000001600146983f7fe983e96dc5b43677fb14822a075596364b136ae0100000000220020dc406857cac1b9d3f53c6986acb78e7774c604281076d10d3fc8e873e18df6d0663e6e0100000000160014d192719c5be868068f023b9383b3cbecb6afbb1639205300000000001976a91412c00ad1271bd4c91e4f8ff11e738c285181399d88ac49844e000000000017a9141ef31e9bc9ccb532b8c01950727990cca190656a877aa148000000000016001404afd9f6e36ba7cadb62133c458a5318f41a33de731b42000000000016001415c0899a887e36f6620619b621855d40b41cbf838b5a3b0000000000220020e65791d3c340710f7aebf117d1f38e1c5383c1df0345a2082e8ebff945e9994646d83a00000000001600140345e587cb42c201c30e505ea9f6f5fb935876ac5df731000000000017a914278dadd16d1314122e4dde67ff9152a61c3a9f4787254f3000000000001600146d095474bec41ccc8ad40f94c74dd25b79b83579e85b7e020000000017a914413b5901fe4e591c95405fd446b5b002da575bf0870000000000000000266a24aa21a9ed2a3f54b40a557c2884545b770a050154a46b5dc132eec3604f01714f413b32a300000000", + "height": 890448, + "timestamp": 1743541701, + "reward": 315238004, + "scriptsig": "0350960d163c204f4345414e2e58595a203e0f0f30304f43423400024087" + }, + "161": { + "jobId": "1611979", + "extraNonce": "000002dc", + "extraNonce2Size": 8, + "prevHash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "coinbase1": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4f0350960d122f62737175617265642fe5006405fe934bb9fabe6d6d2db144ad7b2eed39c89a2cde9f88f06d9082613911584463126673a700303e881000000000000000", + "coinbase2": "ffffffff05220200000000000017a9141bd79ae4d7811daea94bd25db93761b10fecccd7876271cc120000000017a9146547a37cae8db12fd4c8f191f69d4a52cfa886fa870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f524501307f36ff0aff7000ebd4eea1e8e9bbbfa0e1134cb203d7822f404faca98985eb083e8fd9e16ad64700000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "merkleBranches": [ + "15992cde4826516b7dda391bcec363f8da4f6f335199ec76550b535d667c6bcc", + "4703e3571e20a0fa02766ca895c2043bd1c16364b4163a5d7fe3b669fa47b371", + "1d3822d7192f4c8978ca54d5e3b460ab420907199ba2d751ca682b76b9d8e030", + "55b47a1a7ab09d05ea95e4c36b2ece4f441f1787b6548656a67c93850bad28ca", + "1a231983cfb2837a358b85d7bba94851eeedb3f1af915a67165c49ca36c4ea42", + "1ef6e6152101ed2948ef74ebd6d62959f6b21e5c8c404def4faf88976a999c12", + "d28d9eb88badf7d180885592f79da466d0db6dbfd50b440d1c85a2c9ff1b8f01", + "eddef6dd40e0ed35c83129b484f4848021d781cc05da764df8bac3bc82745d12", + "b62ac40bb764914198729c39526751adc6bb047d3f0990ca742c863028a201da", + "7ec1b0edb5aef0b4a71ebf222d22220eb2c38b9fb07603485c9503b65ff43440", + "b4c149bc9f2f48b6887800706e50c669c86af320759914bcd2a53f7f89b92eda", + "c1cde7654ebfad4609b999082eba464c5a20eb1434e46ccd6d81510a13aa853b" + ], + "version": "20000000", + "bits": "1702796c", + "time": "67ec55e6", + "cleanJobs": false, + "received": 1743541734876, + "pool": 161, + "coinbase": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4f0350960d122f62737175617265642fe5006405fe934bb9fabe6d6d2db144ad7b2eed39c89a2cde9f88f06d9082613911584463126673a700303e881000000000000000000002dc0000000000000000ffffffff05220200000000000017a9141bd79ae4d7811daea94bd25db93761b10fecccd7876271cc120000000017a9146547a37cae8db12fd4c8f191f69d4a52cfa886fa870000000000000000266a24aa21a9ed93bbaa9811f4ac5527dcd50345b7b15a3d319778bf43ea027337d6fd660826fe00000000000000002f6a2d434f524501307f36ff0aff7000ebd4eea1e8e9bbbfa0e1134cb203d7822f404faca98985eb083e8fd9e16ad64700000000000000002b6a2952534b424c4f434b3a7ad7ddd490a0e384e30c34942e26100ab1e97b25aa8a53c435370b100070fc9300000000", + "height": 890448, + "timestamp": 1743541734, + "reward": 315388804, + "scriptsig": "0350960d122f62737175617265642fe5006405fe934bb9fabe6d6d2db144ad7b2eed39c89a2cde9f88f06d9082613911584463126673a700303e881000000000000000000002dc0000000000000000" + } + }, + "mempoolInfo": { + "loaded": true, + "size": 4173, + "bytes": 3427792, + "usage": 16974288, + "total_fee": 0.05445324, + "maxmempool": 300000000, + "mempoolminfee": 0.00001, + "minrelaytxfee": 0.00001, + "incrementalrelayfee": 0.00001, + "unbroadcastcount": 0, + "fullrbf": true + }, + "vBytesPerSecond": 1415, + "mempool-blocks": [ + { + "blockSize": 1779311, + "blockVSize": 997968.5, + "nTx": 2132, + "totalFees": 2902870, + "medianFee": 2.0479263387949875, + "feeRange": [ + 1.0721153846153846, + 1.9980563654033041, + 2.2195704057279237, + 3.009493670886076, + 3.4955223880597015, + 6.0246913580246915, + 218.1818181818182 + ] + }, + { + "blockSize": 1959636, + "blockVSize": 997903.5, + "nTx": 497, + "totalFees": 1093076, + "medianFee": 1.102049424602265, + "feeRange": [ + 1.0401794819498267, + 1.0548148148148149, + 1.0548148148148149, + 1.0548148148148149, + 1.0548148148148149, + 1.0761096766260911, + 1.1021605957228275 + ] + }, + { + "blockSize": 1477260, + "blockVSize": 997997.25, + "nTx": 720, + "totalFees": 1016195, + "medianFee": 1.007409072434199, + "feeRange": [ + 1, + 1.0019120458891013, + 1.0040863981319323, + 1.0081019768823594, + 1.018450184501845, + 1.0203327171903882, + 1.0485018498190837 + ] + }, + { + "blockSize": 1021308, + "blockVSize": 431071.5, + "nTx": 823, + "totalFees": 432342, + "medianFee": 0, + "feeRange": [ + 1, + 1, + 1, + 1.0028011204481793, + 1.0042075736325387, + 1.0053475935828877, + 1.0068649885583525 + ] + } + ], + "transactions": [ + { + "txid": "bccfbff8d129df7dcfb65b7587186a807ab2c7e4b8494188ac27733705d9e482", + "fee": 564, + "vsize": 140.25, + "value": 160404, + "rate": 4.021390374331551, + "time": 1743541739 + }, + { + "txid": "94c81b3fc40369520fd7607ed54f18416057aab46bc6da328c0588e5967a2ebd", + "fee": 1044, + "vsize": 315.75, + "value": 64814, + "rate": 3.3064133016627077, + "time": 1743541739 + }, + { + "txid": "a16276de4ce8b2eba0013428af73255060ad04e685bee98731504bf973d49bf7", + "fee": 426, + "vsize": 141.25, + "value": 349522, + "rate": 3.015929203539823, + "time": 1743541739 + }, + { + "txid": "bb25deab784cb213dc4cba234f5a41a6c8950c432a05b5858053b39ceda6d1a3", + "fee": 356, + "vsize": 152.25, + "value": 1562308, + "rate": 2.3382594417077174, + "time": 1743541738 + }, + { + "txid": "17a9e5194345d8da287d49c0bf595474e08fc81434646925d6640964929aa7f9", + "fee": 11400, + "vsize": 189.5, + "value": 3352669, + "rate": 60.15831134564644, + "time": 1743541738 + }, + { + "txid": "ced722a2f9007182b8268240af19fee486e8af07da83efccea6e2374aa484d07", + "fee": 534, + "vsize": 177, + "value": 302121849, + "rate": 3.016949152542373, + "time": 1743541736 + } + ], + "loadingIndicators": {}, + "fees": { + "fastestFee": 3, + "halfHourFee": 3, + "hourFee": 3, + "economyFee": 2, + "minimumFee": 1 + }, + "rbfSummary": [ + { + "txid": "51f468b28c4ee790bdfa3152a3add656f29e908828b55fb295cf55dc6a33a855", + "mined": false, + "fullRbf": false, + "oldFee": 19437, + "oldVsize": 166.5, + "newFee": 19770, + "newVsize": 166.5 + }, + { + "txid": "8fb448ef9a26ca3cee8341e3627adf57ae9374c37b83082ad5d90491955cc38c", + "mined": false, + "fullRbf": false, + "oldFee": 19270, + "oldVsize": 166.25, + "newFee": 19603, + "newVsize": 166.5 + }, + { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "mined": false, + "fullRbf": false, + "oldFee": 606, + "oldVsize": 233, + "newFee": 960, + "newVsize": 224 + }, + { + "txid": "a2b7c8a1e8d0f17108c5089198ccbf6c50341c99abb91959a25a41f9bb44fa60", + "mined": false, + "fullRbf": false, + "oldFee": 504, + "oldVsize": 225, + "newFee": 958, + "newVsize": 226 + }, + { + "txid": "798fb7eb84e69c58a596478436f48d3a620a825624ff88d5f15a0b1e17fa8fdf", + "mined": false, + "fullRbf": false, + "oldFee": 1680, + "oldVsize": 381.25, + "newFee": 2240, + "newVsize": 381.5 + }, + { + "txid": "86dd928e524e12fdfbb6d2ba51e1a0e4d8557d05552184bb20c0427c3e0d19f8", + "mined": false, + "fullRbf": false, + "oldFee": 1476, + "oldVsize": 328.5, + "newFee": 1968, + "newVsize": 328.5 + } + ], + "backend": "esplora", + "blocks": [ + { + "id": "0000000000000000000079f5b74b6533abb0b1ece06570d8d157b5bebd1460b4", + "height": 890440, + "version": 559235072, + "timestamp": 1743535677, + "bits": 386038124, + "nonce": 2920325684, + "difficulty": 113757508810854, + "merkle_root": "c793d5fdbfb1ebe99e14a13a6d65370057d311774d33c71da166663b18722474", + "tx_count": 3823, + "size": 1578209, + "weight": 3993461, + "previousblockhash": "000000000000000000020fb2e24425793e17e60e188205dc1694d221790348b2", + "mediantime": 1743532406, + "stale": false, + "extras": { + "reward": 319838750, + "coinbaseRaw": "0348960d082f5669614254432f2cfabe6d6d294719da11c017243828bf32c405341db7f19387fee92c25413c45e114907f9810000000000000001058bf9601429f9fa7a6c160d10d00000000000000", + "orphans": [], + "medianFee": 4, + "feeRange": [ + 3, + 3, + 3.0191082802547773, + 3.980952380952381, + 5, + 10, + 427.748502994012 + ], + "totalFees": 7338750, + "avgFee": 1920, + "avgFeeRate": 7, + "utxoSetChange": 4093, + "avgTxSize": 412.71000000000004, + "totalInputs": 7430, + "totalOutputs": 11523, + "totalOutputAmt": 547553568373, + "segwitTotalTxs": 3432, + "segwitTotalSize": 1467920, + "segwitTotalWeight": 3552413, + "feePercentiles": null, + "virtualSize": 998365.25, + "coinbaseAddress": "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4", + "coinbaseAddresses": [ + "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4" + ], + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 fb37342f6275b13936799def06f2eb4c0f201515 OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "\u0003H–\r\b/ViaBTC/,ú¾mm)G\u0019Ú\u0011À\u0017$8(¿2Ä\u00054\u001d·ñ“‡þé,%Aìg/Foundry USA Pool #dropgold/\u0001S\nEaç\u0002\u0000\u0000\u0000\u0000\u0000", + "header": "00a03220b46014bdbeb557d1d87065e0ecb1b0ab33654bb7f579000000000000000000003ed60f06cec16df4399b5dafa7077036c2eb58cc6a16e6cdca559b9e2f7e4525bb3eec676c790217b7b3c9cb", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 111, + "name": "Foundry USA", + "slug": "foundryusa", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 2792968, + "expectedWeight": 3991959, + "similarity": 0.9951416839808291 + } + }, + { + "id": "000000000000000000014845978a876b3d8bf5d489b9d87e88873952b06ddfce", + "height": 890442, + "version": 557981696, + "timestamp": 1743536834, + "bits": 386038124, + "nonce": 470697326, + "difficulty": 113757508810854, + "merkle_root": "5e92e681c1db2797a5b3e5016729059f8b60a256cafb51d835dac2b3964c0db4", + "tx_count": 3566, + "size": 1628328, + "weight": 3993552, + "previousblockhash": "00000000000000000000ef5a27459785ea4c91e05f64adfad306af6dfc0cd19c", + "mediantime": 1743532867, + "stale": false, + "extras": { + "reward": 318057766, + "coinbaseRaw": "034a960d194d696e656420627920416e74506f6f6c204d000201e15e2989fabe6d6dd599e9dfa40be51f1517c8f512c5c3d51c7656182f1df335d34b98ee02c527db080000000000000000004f92b702000000000000", + "orphans": [], + "medianFee": 3.00860164711668, + "feeRange": [ + 1.5174418604651163, + 2.0140845070422535, + 2.492354740061162, + 3, + 4.020942408376963, + 7, + 200 + ], + "totalFees": 5557766, + "avgFee": 1558, + "avgFeeRate": 5, + "utxoSetChange": 1971, + "avgTxSize": 456.48, + "totalInputs": 7938, + "totalOutputs": 9909, + "totalOutputAmt": 900044492230, + "segwitTotalTxs": 3214, + "segwitTotalSize": 1526463, + "segwitTotalWeight": 3586200, + "feePercentiles": null, + "virtualSize": 998388, + "coinbaseAddress": "37jKPSmbEGwgfacCr2nayn1wTaqMAbA94Z", + "coinbaseAddresses": [ + "37jKPSmbEGwgfacCr2nayn1wTaqMAbA94Z", + "39C7fxSzEACPjM78Z7xdPxhf7mKxJwvfMJ" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 42402a28dd61f2718a4b27ae72a4791d5bbdade7 OP_EQUAL", + "coinbaseSignatureAscii": "\u0003J–\r\u0019Mined by AntPool M\u0000\u0002\u0001á^)‰ú¾mmՙéߤ\u000bå\u001f\u0015\u0017Èõ\u0012ÅÃÕ\u001cvV\u0018/\u001dó5ÓK˜î\u0002Å'Û\b\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000O’·\u0002\u0000\u0000\u0000\u0000\u0000\u0000", + "header": "002042219cd10cfc6daf06d3faad645fe0914cea859745275aef00000000000000000000b40d4c96b3c2da35d851fbca56a2608b9f05296701e5b3a59727dbc181e6925ec242ec676c7902176e450e1c", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 44, + "name": "AntPool", + "slug": "antpool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 5764747, + "expectedWeight": 3991786, + "similarity": 0.9029319155137951 + } + }, + { + "id": "000000000000000000026e08b270834273511b353bed30d54706211adc96f5f6", + "height": 890443, + "version": 706666496, + "timestamp": 1743537197, + "bits": 386038124, + "nonce": 321696065, + "difficulty": 113757508810854, + "merkle_root": "3d7574f7eca741fa94b4690868a242e5b286f8a0417ad0275d4ab05893e96350", + "tx_count": 2155, + "size": 1700002, + "weight": 3993715, + "previousblockhash": "000000000000000000014845978a876b3d8bf5d489b9d87e88873952b06ddfce", + "mediantime": 1743533789, + "stale": false, + "extras": { + "reward": 315112344, + "coinbaseRaw": "034b960d21202020204d696e656420627920536563706f6f6c2020202070000b05e388958c01fabe6d6db7ae4bfa7b1294e16e800b4563f1f5ddeb5c0740319eba45600f3f05d2d7272910000000000000000000c2cb7e020000", + "orphans": [], + "medianFee": 1.4360674424569184, + "feeRange": [ + 1, + 1.0135135135135136, + 1.09717868338558, + 2.142857142857143, + 3.009584664536741, + 4.831858407079646, + 196.07843137254903 + ], + "totalFees": 2612344, + "avgFee": 1212, + "avgFeeRate": 2, + "utxoSetChange": -2880, + "avgTxSize": 788.64, + "totalInputs": 9773, + "totalOutputs": 6893, + "totalOutputAmt": 264603969671, + "segwitTotalTxs": 1933, + "segwitTotalSize": 1556223, + "segwitTotalWeight": 3418707, + "feePercentiles": null, + "virtualSize": 998428.75, + "coinbaseAddress": "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "coinbaseAddresses": [ + "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "3Awm3FNpmwrbvAFVThRUFqgpbVuqWisni9" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 8ee90177614ecde53314fd67c46162f315852a07 OP_EQUAL", + "coinbaseSignatureAscii": "\u0003K–\r! Mined by Secpool p\u0000\u000b\u0005㈕Œ\u0001ú¾mm·®Kú{\u0012”án€\u000bEcñõÝë\\\u0007@1žºE`\u000f?\u0005Ò×')\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000ÂË~\u0002\u0000\u0000", + "header": "00e01e2acedf6db0523987887ed8b989d4f58b3d6b878a974548010000000000000000005063e99358b04a5d27d07a41a0f886b2e542a2680869b494fa41a7ecf774753d2d44ec676c79021741b12c13", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 141, + "name": "SECPOOL", + "slug": "secpool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 2623934, + "expectedWeight": 3991917, + "similarity": 0.9951244468050102 + } + }, + { + "id": "00000000000000000001b36ec470ae4ec39f5d975f665d6f40e9d35bfa65290d", + "height": 890444, + "version": 671080448, + "timestamp": 1743539347, + "bits": 386038124, + "nonce": 994357124, + "difficulty": 113757508810854, + "merkle_root": "c891d4bf68e22916274b667eb3287d50da2ddd63f8dad892da045cc2ad4a7b21", + "tx_count": 3797, + "size": 1500309, + "weight": 3993525, + "previousblockhash": "000000000000000000026e08b270834273511b353bed30d54706211adc96f5f6", + "mediantime": 1743533986, + "stale": false, + "extras": { + "reward": 318708524, + "coinbaseRaw": "034c960d082f5669614254432f2cfabe6d6d45b7fd7ab53a0914da7dcc9d21fe44f0936f5354169a56df9d5139f07afbc2b41000000000000000106fc0eb03f0ac2e851d18d8d9f85ad70000000000", + "orphans": [], + "medianFee": 4.064775540157046, + "feeRange": [ + 3.014354066985646, + 3.18368700265252, + 3.602836879432624, + 4.231825525040388, + 5.581730769230769, + 10, + 697.7151162790698 + ], + "totalFees": 6208524, + "avgFee": 1635, + "avgFeeRate": 6, + "utxoSetChange": 5755, + "avgTxSize": 395.02, + "totalInputs": 6681, + "totalOutputs": 12436, + "totalOutputAmt": 835839828101, + "segwitTotalTxs": 3351, + "segwitTotalSize": 1354446, + "segwitTotalWeight": 3410181, + "feePercentiles": null, + "virtualSize": 998381.25, + "coinbaseAddress": "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4", + "coinbaseAddresses": [ + "1PuJjnF476W3zXfVYmJfGnouzFDAXakkL4" + ], + "coinbaseSignature": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 fb37342f6275b13936799def06f2eb4c0f201515 OP_EQUALVERIFY OP_CHECKSIG", + "coinbaseSignatureAscii": "\u0003L–\r\b/ViaBTC/,ú¾mmE·ýzµ:\t\u0014Ú}̝!þDð“oST\u0016šVߝQ9ðzû´\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0010oÀë\u0003ð¬.…\u001d\u0018ØÙøZ×\u0000\u0000\u0000\u0000\u0000", + "header": "00e0ff27f6f596dc1a210647d530ed3b351b5173428370b2086e02000000000000000000217b4aadc25c04da92d8daf863dd2dda507d28b37e664b271629e268bfd491c8934cec676c79021784af443b", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 73, + "name": "ViaBTC", + "slug": "viabtc", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 6253024, + "expectedWeight": 3991868, + "similarity": 0.9862862477811569 + } + }, + { + "id": "00000000000000000001402065f940b9475159bdc962c92a66d3c6652ffa338a", + "height": 890445, + "version": 601202688, + "timestamp": 1743539574, + "bits": 386038124, + "nonce": 1647397133, + "difficulty": 113757508810854, + "merkle_root": "61d8294afa8f6bafa4d979a77d187dee5f75a6392f957ea647d96eefbbbc5e9b", + "tx_count": 3579, + "size": 1659862, + "weight": 3993406, + "previousblockhash": "00000000000000000001b36ec470ae4ec39f5d975f665d6f40e9d35bfa65290d", + "mediantime": 1743535677, + "stale": false, + "extras": { + "reward": 315617086, + "coinbaseRaw": "034d960d04764dec672f466f756e6472792055534120506f6f6c202364726f70676f6c642f4fac7c451540000000000000", + "orphans": [], + "medianFee": 2.5565329189526835, + "feeRange": [ + 1.521613832853026, + 2, + 2.2411347517730498, + 3, + 3, + 3.954954954954955, + 162.78343949044586 + ], + "totalFees": 3117086, + "avgFee": 871, + "avgFeeRate": 3, + "utxoSetChange": 1881, + "avgTxSize": 463.65000000000003, + "totalInputs": 7893, + "totalOutputs": 9774, + "totalOutputAmt": 324878597485, + "segwitTotalTxs": 3189, + "segwitTotalSize": 1538741, + "segwitTotalWeight": 3509030, + "feePercentiles": null, + "virtualSize": 998351.5, + "coinbaseAddress": "bc1pp7w6kxnj7lzgm29pmuhezwl0vjdlcrthqukll5gn9xuqfq5n673smy4m63", + "coinbaseAddresses": [ + "bc1pp7w6kxnj7lzgm29pmuhezwl0vjdlcrthqukll5gn9xuqfq5n673smy4m63", + "bc1qwzrryqr3ja8w7hnja2spmkgfdcgvqwp5swz4af4ngsjecfz0w0pqud7k38" + ], + "coinbaseSignature": "OP_PUSHNUM_1 OP_PUSHBYTES_32 0f9dab1a72f7c48da8a1df2f913bef649bfc0d77072dffd11329b8048293d7a3", + "coinbaseSignatureAscii": "\u0003M–\r\u0004vMìg/Foundry USA Pool #dropgold/O¬|E\u0015@\u0000\u0000\u0000\u0000\u0000\u0000", + "header": "00a0d5230d2965fa5bd3e9406f5d665f975d9fc34eae70c46eb3010000000000000000009b5ebcbbef6ed947a67e952f39a6755fee7d187da779d9a4af6b8ffa4a29d861764dec676c7902170d493162", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 111, + "name": "Foundry USA", + "slug": "foundryusa", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 3145370, + "expectedWeight": 3991903, + "similarity": 0.9903353189076812 + } + }, + { + "id": "00000000000000000001fe3d26b02146d0fc2192db06cbc12478d4b347f5306b", + "height": 890446, + "version": 537722880, + "timestamp": 1743541107, + "bits": 386038124, + "nonce": 826569764, + "difficulty": 113757508810854, + "merkle_root": "d9b320d7cb5aace80ca20b934b13b4a272121fbdd59f3aaba690e0326ca2c144", + "tx_count": 3998, + "size": 1541360, + "weight": 3993545, + "previousblockhash": "00000000000000000001402065f940b9475159bdc962c92a66d3c6652ffa338a", + "mediantime": 1743535803, + "stale": false, + "extras": { + "reward": 317976882, + "coinbaseRaw": "034e960d20202020204d696e656420627920536563706f6f6c2020202070001b04fad5fdfefabe6d6d59dd8ebce6e5aab8fb943bbdcede474b6f2d00a395a717970104a6958c17f1ca100000000000000000008089c9350200", + "orphans": [], + "medianFee": 3.3750830641948864, + "feeRange": [ + 2.397163120567376, + 3, + 3, + 3.463647199046484, + 4.49438202247191, + 7.213930348258707, + 476.1904761904762 + ], + "totalFees": 5476882, + "avgFee": 1370, + "avgFeeRate": 5, + "utxoSetChange": 4951, + "avgTxSize": 385.41, + "totalInputs": 7054, + "totalOutputs": 12005, + "totalOutputAmt": 983289729453, + "segwitTotalTxs": 3538, + "segwitTotalSize": 1396505, + "segwitTotalWeight": 3414233, + "feePercentiles": null, + "virtualSize": 998386.25, + "coinbaseAddress": "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "coinbaseAddresses": [ + "3Eif1JfqeMERRsQHtvGEacNN9hhuvnsfe9", + "3Awm3FNpmwrbvAFVThRUFqgpbVuqWisni9" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 8ee90177614ecde53314fd67c46162f315852a07 OP_EQUAL", + "coinbaseSignatureAscii": "\u0003N–\r Mined by Secpool p\u0000\u001b\u0004úÕýþú¾mmYݎ¼æåª¸û”;½ÎÞGKo-\u0000£•§\u0017—\u0001\u0004¦•Œ\u0017ñÊ\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000€‰É5\u0002\u0000", + "header": "00000d208a33fa2f65c6d3662ac962c9bd595147b940f96520400100000000000000000044c1a26c32e090a6ab3a9fd5bd1f1272a2b4134b930ba20ce8ac5acbd720b3d97353ec676c79021724744431", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 141, + "name": "SECPOOL", + "slug": "secpool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 5601814, + "expectedWeight": 3991928, + "similarity": 0.9537877497871488 + } + }, + { + "id": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "height": 890447, + "version": 568860672, + "timestamp": 1743541240, + "bits": 386038124, + "nonce": 4008077709, + "difficulty": 113757508810854, + "merkle_root": "8c3b098e4e50b67075a4fc52bf4cd603aaa450c240c18a865c9ddc0f27104f5f", + "tx_count": 1919, + "size": 1747789, + "weight": 3993172, + "previousblockhash": "00000000000000000001fe3d26b02146d0fc2192db06cbc12478d4b347f5306b", + "mediantime": 1743536834, + "stale": false, + "extras": { + "reward": 314435106, + "coinbaseRaw": "034f960d0f2f736c7573682f65000002fba05ef1fabe6d6df8d29032ea6f9ab1debd223651f30887df779c6195869e70a7b787b3a15f4b1710000000000000000000ee5f0c00b20200000000", + "orphans": [], + "medianFee": 1.4653828213500366, + "feeRange": [ + 1.0845070422535212, + 1.2, + 1.51, + 2.0141129032258065, + 2.3893805309734515, + 4.025477707006369, + 300.0065359477124 + ], + "totalFees": 1935106, + "avgFee": 1008, + "avgFeeRate": 1, + "utxoSetChange": -4244, + "avgTxSize": 910.58, + "totalInputs": 9909, + "totalOutputs": 5665, + "totalOutputAmt": 210763861504, + "segwitTotalTxs": 1720, + "segwitTotalSize": 1629450, + "segwitTotalWeight": 3519924, + "feePercentiles": null, + "virtualSize": 998293, + "coinbaseAddress": "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11", + "coinbaseAddresses": [ + "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 1f0cbbec8bc4c945e4e16249b11eee911eded55f OP_EQUAL", + "coinbaseSignatureAscii": "\u0003O–\r\u000f/slush/e\u0000\u0000\u0002û ^ñú¾mmøÒ2êoš±Þ½\"6Qó\b‡ßwœa•†žp§·‡³¡_K\u0017\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000î_\f\u0000²\u0002\u0000\u0000\u0000\u0000", + "header": "0020e8216b30f547b3d47824c1cb06db9221fcd04621b0263dfe010000000000000000005f4f10270fdc9d5c868ac140c250a4aa03d64cbf52fca47570b6504e8e093b8cf853ec676c7902178d69e6ee", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 43, + "name": "Braiins Pool", + "slug": "braiinspool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 2059571, + "expectedWeight": 3991720, + "similarity": 0.9149852183486826 + } + } + ], + "conversions": { + "time": 1743541504, + "USD": 85181, + "EUR": 78939, + "GBP": 65914, + "CAD": 121813, + "CHF": 75399, + "AUD": 135492, + "JPY": 12753000 + }, + "backendInfo": { + "hostname": "node205.tk7.mempool.space", + "version": "3.1.0-dev", + "gitCommit": "3c08b5c72", + "lightning": false, + "backend": "esplora" + }, + "da": { + "progressPercent": 68.99801587301587, + "difficultyChange": 2.7058667283164084, + "estimatedRetargetDate": 1743907120500, + "remainingBlocks": 625, + "remainingTime": 365382500, + "previousRetarget": 1.433804484570416, + "previousTime": 1742728542, + "nextRetargetHeight": 891072, + "timeAvg": 584612, + "adjustedTimeAvg": 584612, + "timeOffset": 0, + "expectedBlocks": 1355.3266666666666 + } +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx01_ws_tx_replaced.json b/frontend/cypress/fixtures/details_rbf/tx01_ws_tx_replaced.json new file mode 100644 index 000000000..7af723546 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx01_ws_tx_replaced.json @@ -0,0 +1,5 @@ +{ + "txReplaced": { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698" + } +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_api_cpfp.json b/frontend/cypress/fixtures/details_rbf/tx02_api_cpfp.json new file mode 100644 index 000000000..478eecfbc --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_api_cpfp.json @@ -0,0 +1,9 @@ +{ + "ancestors": [], + "bestDescendant": null, + "descendants": [], + "effectiveFeePerVsize": 4.285714285714286, + "sigops": 4, + "fee": 960, + "adjustedVsize": 224 +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_api_rbf.json b/frontend/cypress/fixtures/details_rbf/tx02_api_rbf.json new file mode 100644 index 000000000..96bd1efdd --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_api_rbf.json @@ -0,0 +1,36 @@ +{ + "replacements": { + "tx": { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "fee": 960, + "vsize": 224, + "value": 49040, + "rate": 4.285714285714286, + "time": 1743541726, + "rbf": true, + "fullRbf": false + }, + "time": 1743541726, + "fullRbf": false, + "replaces": [ + { + "tx": { + "txid": "242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29", + "fee": 606, + "vsize": 233, + "value": 49394, + "rate": 2.6008583690987126, + "time": 1743541407, + "rbf": true + }, + "time": 1743541407, + "interval": 319, + "fullRbf": false, + "replaces": [] + } + ] + }, + "replaces": [ + "242f3fff9ca7d5aea7a7a57d886f3fa7329e24fac948598a991b3a3dd631cd29" + ] +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_api_tx.json b/frontend/cypress/fixtures/details_rbf/tx02_api_tx.json new file mode 100644 index 000000000..cbcb4fe8c --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_api_tx.json @@ -0,0 +1,38 @@ +{ + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "version": 2, + "locktime": 0, + "vin": [ + { + "txid": "fb16c141d22a3a7af5e44e7478a8663866c35d554cd85107ec8a99b97e5a72e9", + "vout": 0, + "prevout": { + "scriptpubkey": "76a914099f831c49289c7b9cc07a9a632867ecd51a105a88ac", + "scriptpubkey_asm": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 099f831c49289c7b9cc07a9a632867ecd51a105a OP_EQUALVERIFY OP_CHECKSIG", + "scriptpubkey_type": "p2pkh", + "scriptpubkey_address": "1stAprEZapjCYGUACUcXohQqSHF9MU5Kj", + "value": 50000 + }, + "scriptsig": "483045022100ab59cf33b33eab1f76286a0fa6962c12171f2133931fec3616f96883551e6c9d02205cacbae788359f2a32ff629e732be0d95843440a3d1c222a63095f83fb69f438014104ea83ffe426ee6b6827029c72fbdcb0a1602829c1fe384637a7d178aa62a6a1e3d7f29250e001ee708a93ca9771f50ee638aaaaef8941c8a7e2bad5494b23e0df", + "scriptsig_asm": "OP_PUSHBYTES_72 3045022100ab59cf33b33eab1f76286a0fa6962c12171f2133931fec3616f96883551e6c9d02205cacbae788359f2a32ff629e732be0d95843440a3d1c222a63095f83fb69f43801 OP_PUSHBYTES_65 04ea83ffe426ee6b6827029c72fbdcb0a1602829c1fe384637a7d178aa62a6a1e3d7f29250e001ee708a93ca9771f50ee638aaaaef8941c8a7e2bad5494b23e0df", + "is_coinbase": false, + "sequence": 4294967293 + } + ], + "vout": [ + { + "scriptpubkey": "76a914099f831c49289c7b9cc07a9a632867ecd51a105a88ac", + "scriptpubkey_asm": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 099f831c49289c7b9cc07a9a632867ecd51a105a OP_EQUALVERIFY OP_CHECKSIG", + "scriptpubkey_type": "p2pkh", + "scriptpubkey_address": "1stAprEZapjCYGUACUcXohQqSHF9MU5Kj", + "value": 49040 + } + ], + "size": 224, + "weight": 896, + "sigops": 4, + "fee": 960, + "status": { + "confirmed": false + } +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_api_tx_times.json b/frontend/cypress/fixtures/details_rbf/tx02_api_tx_times.json new file mode 100644 index 000000000..365c3885e --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_api_tx_times.json @@ -0,0 +1,3 @@ +[ + 1743541726 +] \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_ws_block.json b/frontend/cypress/fixtures/details_rbf/tx02_ws_block.json new file mode 100644 index 000000000..9fc358795 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_ws_block.json @@ -0,0 +1,116 @@ +{ + "block": { + "id": "000000000000000000019bfe551da67e0d93dda4b355d16f1fdb4031ba7e5d61", + "height": 890448, + "version": 626941952, + "timestamp": 1743541850, + "bits": 386038124, + "nonce": 1177284424, + "difficulty": 113757508810854, + "merkle_root": "563d862ef4ec95ea97735ca5561e347ca6e216cb56bd451e8bedb1014078d6c3", + "tx_count": 2229, + "size": 1763153, + "weight": 3993275, + "previousblockhash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "mediantime": 1743537197, + "stale": false, + "extras": { + "reward": 315498786, + "coinbaseRaw": "0350960d0f2f736c7573682fe500c702ff43d142fabe6d6def42d9effd800b9839c832dcfdc6899e137547f15c8a598dbe3a1363f999a0a310000000000000000000e9a2050064dc00000000", + "orphans": [], + "medianFee": 2.144206217858874, + "feeRange": [ + 1.0845921450151057, + 2, + 2.2448979591836733, + 3, + 3.5985915492957745, + 6, + 217.0212765957447 + ], + "totalFees": 2998786, + "avgFee": 1345, + "avgFeeRate": 3, + "utxoSetChange": -3073, + "avgTxSize": 790.84, + "totalInputs": 9558, + "totalOutputs": 6485, + "totalOutputAmt": 442206797883, + "segwitTotalTxs": 1986, + "segwitTotalSize": 1676431, + "segwitTotalWeight": 3646495, + "feePercentiles": null, + "virtualSize": 998318.75, + "coinbaseAddress": "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11", + "coinbaseAddresses": [ + "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 1f0cbbec8bc4c945e4e16249b11eee911eded55f OP_EQUAL", + "coinbaseSignatureAscii": "\u0003P–\r\u000f/slush/å\u0000Ç\u0002ÿCÑBú¾mmïBÙïý€\u000b˜9È2ÜýƉž\u0013uGñ\\ŠY¾:\u0013cù™ £\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000é¢\u0005\u0000dÜ\u0000\u0000\u0000\u0000", + "header": "00605e25abe2e310c10e724cfb641859658fee6570ec6062cc0400000000000000000000c3d6784001b1ed8b1e45bd56cb16e2a67c341e56a55c7397ea95ecf42e863d565a56ec676c79021748ef2b46", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 43, + "name": "Braiins Pool", + "slug": "braiinspool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 3287140, + "expectedWeight": 3991809, + "similarity": 0.9079894021278392 + } + }, + "mempool-blocks": [ + { + "blockSize": 1979907, + "blockVSize": 997974.75, + "nTx": 461, + "totalFees": 1429178, + "medianFee": 1.1020797417745662, + "feeRange": [ + 1.0666666666666667, + 1.0746847720659554, + 1.102059530141031, + 3, + 3.4233409610983982, + 5.017605633802817, + 148.4084084084084 + ] + }, + { + "blockSize": 1363691, + "blockVSize": 997986.5, + "nTx": 1080, + "totalFees": 1023741, + "medianFee": 1.014827018121911, + "feeRange": [ + 1, + 1.0036011703803736, + 1.0054683365672958, + 1.0186757215619695, + 1.0548148148148149, + 1.0548148148148149, + 1.068146618482189 + ] + }, + { + "blockSize": 1337253, + "blockVSize": 563516.25, + "nTx": 901, + "totalFees": 564834, + "medianFee": 1.0028011204481793, + "feeRange": [ + 1, + 1, + 1, + 1.0025062656641603, + 1.004231311706629, + 1.0053475935828877, + 1.0068649885583525 + ] + } + ], + "txConfirmed": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698" +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_ws_block_confirmation.json b/frontend/cypress/fixtures/details_rbf/tx02_ws_block_confirmation.json new file mode 100644 index 000000000..9fc358795 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_ws_block_confirmation.json @@ -0,0 +1,116 @@ +{ + "block": { + "id": "000000000000000000019bfe551da67e0d93dda4b355d16f1fdb4031ba7e5d61", + "height": 890448, + "version": 626941952, + "timestamp": 1743541850, + "bits": 386038124, + "nonce": 1177284424, + "difficulty": 113757508810854, + "merkle_root": "563d862ef4ec95ea97735ca5561e347ca6e216cb56bd451e8bedb1014078d6c3", + "tx_count": 2229, + "size": 1763153, + "weight": 3993275, + "previousblockhash": "0000000000000000000004cc6260ec7065ee8f65591864fb4c720ec110e3e2ab", + "mediantime": 1743537197, + "stale": false, + "extras": { + "reward": 315498786, + "coinbaseRaw": "0350960d0f2f736c7573682fe500c702ff43d142fabe6d6def42d9effd800b9839c832dcfdc6899e137547f15c8a598dbe3a1363f999a0a310000000000000000000e9a2050064dc00000000", + "orphans": [], + "medianFee": 2.144206217858874, + "feeRange": [ + 1.0845921450151057, + 2, + 2.2448979591836733, + 3, + 3.5985915492957745, + 6, + 217.0212765957447 + ], + "totalFees": 2998786, + "avgFee": 1345, + "avgFeeRate": 3, + "utxoSetChange": -3073, + "avgTxSize": 790.84, + "totalInputs": 9558, + "totalOutputs": 6485, + "totalOutputAmt": 442206797883, + "segwitTotalTxs": 1986, + "segwitTotalSize": 1676431, + "segwitTotalWeight": 3646495, + "feePercentiles": null, + "virtualSize": 998318.75, + "coinbaseAddress": "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11", + "coinbaseAddresses": [ + "34XC8GbijKCCvppNvhw4Ra8QZdWsg8tC11" + ], + "coinbaseSignature": "OP_HASH160 OP_PUSHBYTES_20 1f0cbbec8bc4c945e4e16249b11eee911eded55f OP_EQUAL", + "coinbaseSignatureAscii": "\u0003P–\r\u000f/slush/å\u0000Ç\u0002ÿCÑBú¾mmïBÙïý€\u000b˜9È2ÜýƉž\u0013uGñ\\ŠY¾:\u0013cù™ £\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000é¢\u0005\u0000dÜ\u0000\u0000\u0000\u0000", + "header": "00605e25abe2e310c10e724cfb641859658fee6570ec6062cc0400000000000000000000c3d6784001b1ed8b1e45bd56cb16e2a67c341e56a55c7397ea95ecf42e863d565a56ec676c79021748ef2b46", + "utxoSetSize": null, + "totalInputAmt": null, + "pool": { + "id": 43, + "name": "Braiins Pool", + "slug": "braiinspool", + "minerNames": null + }, + "matchRate": 100, + "expectedFees": 3287140, + "expectedWeight": 3991809, + "similarity": 0.9079894021278392 + } + }, + "mempool-blocks": [ + { + "blockSize": 1979907, + "blockVSize": 997974.75, + "nTx": 461, + "totalFees": 1429178, + "medianFee": 1.1020797417745662, + "feeRange": [ + 1.0666666666666667, + 1.0746847720659554, + 1.102059530141031, + 3, + 3.4233409610983982, + 5.017605633802817, + 148.4084084084084 + ] + }, + { + "blockSize": 1363691, + "blockVSize": 997986.5, + "nTx": 1080, + "totalFees": 1023741, + "medianFee": 1.014827018121911, + "feeRange": [ + 1, + 1.0036011703803736, + 1.0054683365672958, + 1.0186757215619695, + 1.0548148148148149, + 1.0548148148148149, + 1.068146618482189 + ] + }, + { + "blockSize": 1337253, + "blockVSize": 563516.25, + "nTx": 901, + "totalFees": 564834, + "medianFee": 1.0028011204481793, + "feeRange": [ + 1, + 1, + 1, + 1.0025062656641603, + 1.004231311706629, + 1.0053475935828877, + 1.0068649885583525 + ] + } + ], + "txConfirmed": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698" +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_ws_mempool_blocks_01.json b/frontend/cypress/fixtures/details_rbf/tx02_ws_mempool_blocks_01.json new file mode 100644 index 000000000..4c83cf797 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_ws_mempool_blocks_01.json @@ -0,0 +1,75 @@ +{ + "mempool-blocks": [ + { + "blockSize": 1779823, + "blockVSize": 997995.25, + "nTx": 2133, + "totalFees": 2922926, + "medianFee": 2.0479263387949875, + "feeRange": [ + 1.0825892857142858, + 2, + 2.2439024390243905, + 3.010452961672474, + 3.554973821989529, + 6.032085561497326, + 218.1818181818182 + ] + }, + { + "blockSize": 1957833, + "blockVSize": 997953, + "nTx": 500, + "totalFees": 1093270, + "medianFee": 1.102049424602265, + "feeRange": [ + 1.0548148148148149, + 1.0548148148148149, + 1.0548148148148149, + 1.0548148148148149, + 1.067677314564158, + 1.0766488413547237, + 1.1021605957228275 + ] + }, + { + "blockSize": 1477864, + "blockVSize": 997999, + "nTx": 730, + "totalFees": 1016458, + "medianFee": 1.0075971559364956, + "feeRange": [ + 1, + 1.0019552465783186, + 1.004255319148936, + 1.0081019768823594, + 1.018450184501845, + 1.0203327171903882, + 1.0548148148148149 + ] + }, + { + "blockSize": 1030954, + "blockVSize": 436613.5, + "nTx": 838, + "totalFees": 437891, + "medianFee": 0, + "feeRange": [ + 1, + 1, + 1, + 1.0026525198938991, + 1.004231311706629, + 1.0053475935828877, + 1.0068649885583525 + ] + } + ], + "txPosition": { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "position": { + "block": 0, + "vsize": 111102 + } + } +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_ws_next_block.json b/frontend/cypress/fixtures/details_rbf/tx02_ws_next_block.json new file mode 100644 index 000000000..4245d4611 --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_ws_next_block.json @@ -0,0 +1,75 @@ +{ + "mempool-blocks": [ + { + "blockSize": 1719945, + "blockVSize": 997952.25, + "nTx": 2558, + "totalFees": 3287140, + "medianFee": 2.4046448299072485, + "feeRange": [ + 1.073446327683616, + 2, + 2.2567567567567566, + 3.0106761565836297, + 3.6169014084507043, + 6.015037593984962, + 218.1818181818182 + ] + }, + { + "blockSize": 2022898, + "blockVSize": 997983.25, + "nTx": 131, + "totalFees": 1098129, + "medianFee": 1.1020797417745662, + "feeRange": [ + 1.0625, + 1.0691217722793642, + 1.073436083408885, + 1.0761096766260911, + 1.080091533180778, + 1.102110739151618, + 1.1021909190121146 + ] + }, + { + "blockSize": 1363844, + "blockVSize": 997998.5, + "nTx": 1073, + "totalFees": 1023651, + "medianFee": 1.014827018121911, + "feeRange": [ + 1, + 1.003584229390681, + 1.0054683365672958, + 1.0186757215619695, + 1.0548148148148149, + 1.0548148148148149, + 1.068146618482189 + ] + }, + { + "blockSize": 1335390, + "blockVSize": 562453.5, + "nTx": 902, + "totalFees": 563772, + "medianFee": 1.0028011204481793, + "feeRange": [ + 1, + 1, + 1, + 1.0025402201524132, + 1.004231311706629, + 1.0053475935828877, + 1.0068649885583525 + ] + } + ], + "txPosition": { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "position": { + "block": 0, + "vsize": 128920 + } + } +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/details_rbf/tx02_ws_tx_position.json b/frontend/cypress/fixtures/details_rbf/tx02_ws_tx_position.json new file mode 100644 index 000000000..c4a2db35a --- /dev/null +++ b/frontend/cypress/fixtures/details_rbf/tx02_ws_tx_position.json @@ -0,0 +1,9 @@ +{ + "txPosition": { + "txid": "b6a53d8a8025c0c5b194345925a99741b645cae0b1f180f0613273029f2bf698", + "position": { + "block": 0, + "vsize": 110880 + } + } +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/rbf_page/rbf_01.json b/frontend/cypress/fixtures/rbf_page/rbf_01.json new file mode 100644 index 000000000..2c79f142a --- /dev/null +++ b/frontend/cypress/fixtures/rbf_page/rbf_01.json @@ -0,0 +1,37 @@ +{ + "rbfLatest": [ + { + "tx": { + "txid": "f4bae4f626036250fd00d68490e572f65f66417452003a0f4c4d76f17a9fde68", + "fee": 1185, + "vsize": 223, + "value": 41729, + "rate": 5.313901345291479, + "time": 1743587177, + "rbf": true, + "fullRbf": false, + "mined": true + }, + "time": 1743587177, + "fullRbf": true, + "replaces": [ + { + "tx": { + "txid": "12945412dfc455e0ed6049dc2ee8737756c8d9e2d9a2eb26f366cd5019a0369f", + "fee": 504, + "vsize": 222, + "value": 42410, + "rate": 2.27027027027027, + "time": 1743586081, + "rbf": true + }, + "time": 1743586081, + "interval": 1096, + "fullRbf": false, + "replaces": [] + } + ], + "mined": true + } + ] +} \ No newline at end of file diff --git a/frontend/cypress/fixtures/rbf_page/rbf_02.json b/frontend/cypress/fixtures/rbf_page/rbf_02.json new file mode 100644 index 000000000..3b5011002 --- /dev/null +++ b/frontend/cypress/fixtures/rbf_page/rbf_02.json @@ -0,0 +1,68 @@ +{ + "rbfLatest": [ + { + "tx": { + "txid": "d313b479acfbae719afb488a078e0fe0e052a67b9f65f73f7c75d3d95fd36acc", + "fee": 672, + "vsize": 167.25, + "value": 29996328, + "rate": 4.017937219730942, + "time": 1743587365, + "rbf": true, + "fullRbf": false + }, + "time": 1743587365, + "fullRbf": false, + "replaces": [ + { + "tx": { + "txid": "eb5aa786cabda307cc9642cfb9c41a3b405ac20a391eefbe54be7930bea61865", + "fee": 336, + "vsize": 167.5, + "value": 29996664, + "rate": 2.005970149253731, + "time": 1743586424, + "rbf": true + }, + "time": 1743586424, + "interval": 941, + "fullRbf": false, + "replaces": [] + } + ] + }, + { + "tx": { + "txid": "f4bae4f626036250fd00d68490e572f65f66417452003a0f4c4d76f17a9fde68", + "fee": 1185, + "vsize": 223, + "value": 41729, + "rate": 5.313901345291479, + "time": 1743587177, + "rbf": true, + "fullRbf": false, + "mined": true + }, + "time": 1743587177, + "fullRbf": true, + "replaces": [ + { + "tx": { + "txid": "12945412dfc455e0ed6049dc2ee8737756c8d9e2d9a2eb26f366cd5019a0369f", + "fee": 504, + "vsize": 222, + "value": 42410, + "rate": 2.27027027027027, + "time": 1743586081, + "rbf": true + }, + "time": 1743586081, + "interval": 1096, + "fullRbf": false, + "replaces": [] + } + ], + "mined": true + } + ] +} \ No newline at end of file diff --git a/frontend/cypress/support/commands.ts b/frontend/cypress/support/commands.ts index 018f63569..2ce198241 100644 --- a/frontend/cypress/support/commands.ts +++ b/frontend/cypress/support/commands.ts @@ -44,6 +44,7 @@ import { PageIdleDetector } from './PageIdleDetector'; import { mockWebSocket } from './websocket'; +import { mockWebSocketV2 } from './websocket'; /* global Cypress */ const codes = { @@ -72,6 +73,10 @@ Cypress.Commands.add('mockMempoolSocket', () => { mockWebSocket(); }); +Cypress.Commands.add('mockMempoolSocketV2', () => { + mockWebSocketV2(); +}); + Cypress.Commands.add('changeNetwork', (network: "testnet" | "testnet4" | "signet" | "liquid" | "mainnet") => { cy.get('.dropdown-toggle').click().then(() => { cy.get(`a.${network}`).click().then(() => { diff --git a/frontend/cypress/support/index.d.ts b/frontend/cypress/support/index.d.ts index 2c5328301..21ffe6a2d 100644 --- a/frontend/cypress/support/index.d.ts +++ b/frontend/cypress/support/index.d.ts @@ -5,6 +5,7 @@ declare namespace Cypress { waitForSkeletonGone(): Chainable waitForPageIdle(): Chainable mockMempoolSocket(): Chainable + mockMempoolSocketV2(): Chainable changeNetwork(network: "testnet"|"testnet4"|"signet"|"liquid"|"mainnet"): Chainable } } \ No newline at end of file diff --git a/frontend/cypress/support/websocket.ts b/frontend/cypress/support/websocket.ts index 1356ccc76..b067cc6e8 100644 --- a/frontend/cypress/support/websocket.ts +++ b/frontend/cypress/support/websocket.ts @@ -27,6 +27,37 @@ const createMock = (url: string) => { return mocks[url]; }; +export const mockWebSocketV2 = () => { + cy.on('window:before:load', (win) => { + const winWebSocket = win.WebSocket; + cy.stub(win, 'WebSocket').callsFake((url) => { + console.log(url); + if ((new URL(url).pathname.indexOf('/sockjs-node/') !== 0)) { + const { server, websocket } = createMock(url); + + win.mockServer = server; + win.mockServer.on('connection', (socket) => { + win.mockSocket = socket; + }); + + win.mockServer.on('message', (message) => { + console.log(message); + }); + + return websocket; + } else { + return new winWebSocket(url); + } + }); + }); + + cy.on('window:before:unload', () => { + for (const url in mocks) { + cleanupMock(url); + } + }); +}; + export const mockWebSocket = () => { cy.on('window:before:load', (win) => { const winWebSocket = win.WebSocket; @@ -65,6 +96,27 @@ export const mockWebSocket = () => { }); }; +export const receiveWebSocketMessageFromServer = ({ + params +}: { params?: any } = {}) => { + cy.window().then((win) => { + if (params.message) { + console.log('sending message'); + win.mockSocket.send(params.message.contents); + } + + if (params.file) { + cy.readFile(`cypress/fixtures/${params.file.path}`, 'utf-8').then((fixture) => { + console.log('sending payload'); + win.mockSocket.send(JSON.stringify(fixture)); + }); + + } + }); + return; +}; + + export const emitMempoolInfo = ({ params }: { params?: any } = {}) => { @@ -82,16 +134,22 @@ export const emitMempoolInfo = ({ switch (params.command) { case "init": { win.mockSocket.send('{"conversions":{"USD":32365.338815782445}}'); - cy.readFile('cypress/fixtures/mainnet_live2hchart.json', 'ascii').then((fixture) => { + cy.readFile('cypress/fixtures/mainnet_live2hchart.json', 'utf-8').then((fixture) => { win.mockSocket.send(JSON.stringify(fixture)); }); - cy.readFile('cypress/fixtures/mainnet_mempoolInfo.json', 'ascii').then((fixture) => { + cy.readFile('cypress/fixtures/mainnet_mempoolInfo.json', 'utf-8').then((fixture) => { win.mockSocket.send(JSON.stringify(fixture)); }); break; } case "rbfTransaction": { - cy.readFile('cypress/fixtures/mainnet_rbf.json', 'ascii').then((fixture) => { + cy.readFile('cypress/fixtures/mainnet_rbf.json', 'utf-8').then((fixture) => { + win.mockSocket.send(JSON.stringify(fixture)); + }); + break; + } + case 'trackTx': { + cy.readFile('cypress/fixtures/track_tx.json', 'utf-8').then((fixture) => { win.mockSocket.send(JSON.stringify(fixture)); }); break; diff --git a/frontend/package.json b/frontend/package.json index b50085a54..8d4ee6e27 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,14 +25,12 @@ "i18n-extract-from-source": "npm run ng -- extract-i18n --out-file ./src/locale/messages.xlf", "i18n-pull-from-transifex": "tx pull -a --parallel --minimum-perc 1 --force", "serve": "npm run generate-config && npm run ng -- serve -c local", - "serve:stg": "npm run generate-config && npm run ng -- serve -c staging", "serve:local-prod": "npm run generate-config && npm run ng -- serve -c local-prod", - "serve:local-staging": "npm run generate-config && npm run ng -- serve -c local-staging", + "serve:parameterized": "npm run generate-config && npm run ng -- serve -c parameterized", "start": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local", + "start:parameterized": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c parameterized", "start:local-esplora": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-esplora", - "start:stg": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c staging", "start:local-prod": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-prod", - "start:local-staging": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c local-staging", "start:mixed": "npm run generate-config && npm run sync-assets-dev && npm run ng -- serve -c mixed", "build": "npm run generate-config && npm run ng -- build --configuration production --localize && npm run sync-assets-dev && npm run sync-assets && npm run build-mempool.js", "sync-assets": "rsync -av ./src/resources ./dist/mempool/browser && node sync-assets.js 'dist/mempool/browser/resources/'", @@ -58,8 +56,8 @@ "cypress:run:record": "cypress run --record", "cypress:open:ci": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:open", "cypress:run:ci": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record", - "cypress:open:ci:staging": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:open", - "cypress:run:ci:staging": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-staging 4200 cypress:run:record" + "cypress:open:ci:parameterized": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:parameterized 4200 cypress:open", + "cypress:run:ci:parameterized": "node update-config.js TESTNET_ENABLED=true TESTNET4_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:parameterized 4200 cypress:run:record" }, "dependencies": { "@angular-devkit/build-angular": "^17.3.1", @@ -123,4 +121,4 @@ "scarfSettings": { "enabled": false } -} +} \ No newline at end of file diff --git a/frontend/proxy.conf.parameterized.js b/frontend/proxy.conf.parameterized.js new file mode 100644 index 000000000..eee9bf1f7 --- /dev/null +++ b/frontend/proxy.conf.parameterized.js @@ -0,0 +1,36 @@ +const fs = require('fs'); + +const PROXY_CONFIG = require('./proxy.conf'); + +const addApiKeyHeader = (proxyReq, req, res) => { + if (process.env.MEMPOOL_CI_API_KEY) { + proxyReq.setHeader('X-Mempool-Auth', process.env.MEMPOOL_CI_API_KEY); + } +}; + +PROXY_CONFIG.forEach((entry) => { + const mempoolHostname = process.env.MEMPOOL_HOSTNAME + ? process.env.MEMPOOL_HOSTNAME + : 'mempool.space'; + + const liquidHostname = process.env.LIQUID_HOSTNAME + ? process.env.LIQUID_HOSTNAME + : 'liquid.network'; + + entry.target = entry.target.replace('mempool.space', mempoolHostname); + entry.target = entry.target.replace('liquid.network', liquidHostname); + + if (entry.onProxyReq) { + const originalProxyReq = entry.onProxyReq; + entry.onProxyReq = (proxyReq, req, res) => { + originalProxyReq(proxyReq, req, res); + if (process.env.MEMPOOL_CI_API_KEY) { + proxyReq.setHeader('X-Mempool-Auth', process.env.MEMPOOL_CI_API_KEY); + } + }; + } else { + entry.onProxyReq = addApiKeyHeader; + } +}); + +module.exports = PROXY_CONFIG; diff --git a/frontend/proxy.conf.staging.js b/frontend/proxy.conf.staging.js deleted file mode 100644 index 0165bed96..000000000 --- a/frontend/proxy.conf.staging.js +++ /dev/null @@ -1,12 +0,0 @@ -const fs = require('fs'); - -let PROXY_CONFIG = require('./proxy.conf'); - -PROXY_CONFIG.forEach(entry => { - const hostname = process.env.CYPRESS_REROUTE_TESTNET === 'true' ? 'mempool-staging.fra.mempool.space' : 'node201.va1.mempool.space'; - console.log(`e2e tests running against ${hostname}`); - entry.target = entry.target.replace("mempool.space", hostname); - entry.target = entry.target.replace("liquid.network", "liquid-staging.va1.mempool.space"); -}); - -module.exports = PROXY_CONFIG; diff --git a/frontend/src/app/components/about/about.component.html b/frontend/src/app/components/about/about.component.html index 3bd8960f5..8ac931d7a 100644 --- a/frontend/src/app/components/about/about.component.html +++ b/frontend/src/app/components/about/about.component.html @@ -147,6 +147,15 @@ Bull Bitcoin + + + Fortris + @@ -451,7 +460,7 @@ Trademark Notice

- The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo, the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries. + The Mempool Open Source Project®, Mempool Accelerator®, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool Logo, the mempool Square Logo, the mempool block visualization Logo, the mempool Blocks Logo, the mempool transaction Logo, the mempool Blocks 3 | 2 Logo, the mempool research Logo, the mempool.space Vertical Logo, and the mempool.space Horizontal Logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries.

While our software is available under an open source software license, the copyright license does not include an implied right or license to use our trademarks. See our Trademark Policy and Guidelines for more details, published on <https://mempool.space/trademark-policy>. diff --git a/frontend/src/app/components/about/about.component.scss b/frontend/src/app/components/about/about.component.scss index 6a76bf299..e756473a6 100644 --- a/frontend/src/app/components/about/about.component.scss +++ b/frontend/src/app/components/about/about.component.scss @@ -264,6 +264,6 @@ display: flex; flex-wrap: wrap; justify-content: center; - max-width: 800px; + max-width: 850px; } -} \ No newline at end of file +} diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html index 4594fd9fc..4601823dc 100644 --- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html +++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.html @@ -158,7 +158,7 @@ - Mempool Accelerator™ fees + Mempool Accelerator® fees @@ -567,14 +567,29 @@ } @else if (step === 'success') {

-

Your transaction is being accelerated!

+

+ @if (accelerationResponse) { + Your transaction is being accelerated! + } @else { + Transaction is already being accelerated! + } +

- Your transaction has been accepted for acceleration by our mining pool partners. + @if (accelerationResponse) { + Your transaction has been accepted for acceleration by our mining pool partners. + } @else { + Transaction has already been accepted for acceleration by our mining pool partners. + }
+ @if (accelerationResponse?.receiptUrl) { + + }

diff --git a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts index ac6c7f147..fda32e065 100644 --- a/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts +++ b/frontend/src/app/components/accelerate-checkout/accelerate-checkout.component.ts @@ -87,6 +87,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { math = Math; isMobile: boolean = window.innerWidth <= 767.98; isProdDomain = false; + accelerationResponse: { receiptUrl: string | null } | undefined; private _step: CheckoutStep = 'summary'; simpleMode: boolean = true; @@ -194,16 +195,12 @@ export class AccelerateCheckout implements OnInit, OnDestroy { this.scrollToElement('acceleratePreviewAnchor', 'start'); } if (changes.accelerating && this.accelerating) { - if (this.step === 'processing' || this.step === 'paid') { - this.moveToStep('success', true); - } else { // Edge case where the transaction gets accelerated by someone else or on another session - this.closeModal(); - } + this.moveToStep('success', true); } } moveToStep(step: CheckoutStep, force: boolean = false): void { - if (this.isCheckoutLocked > 0 && !force) { + if (this.isCheckoutLocked > 0 && !force || this.step === 'success') { return; } this.processing = false; @@ -525,7 +522,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { if (tokenResult?.status === 'OK') { const card = tokenResult.details?.card; if (!card || !card.brand || !card.expMonth || !card.expYear || !card.last4) { - console.error(`Cannot retreive payment card details`); + console.error(`Cannot retrieve payment card details`); this.accelerateError = 'apple_pay_no_card_details'; this.processing = false; return; @@ -541,7 +538,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, costUSD ).subscribe({ - next: () => { + next: (response) => { + this.accelerationResponse = response; this.processing = false; this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); this.audioService.playSound('ascend-chime-cartoon'); @@ -643,7 +641,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { if (tokenResult?.status === 'OK') { const card = tokenResult.details?.card; if (!card || !card.brand || !card.expMonth || !card.expYear || !card.last4) { - console.error(`Cannot retreive payment card details`); + console.error(`Cannot retrieve payment card details`); this.accelerateError = 'apple_pay_no_card_details'; this.processing = false; return; @@ -668,7 +666,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { costUSD, verificationToken.userChallenged ).subscribe({ - next: () => { + next: (response) => { + this.accelerationResponse = response; this.processing = false; this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); this.audioService.playSound('ascend-chime-cartoon'); @@ -777,7 +776,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { costUSD, verificationToken.userChallenged ).subscribe({ - next: () => { + next: (response) => { + this.accelerationResponse = response; this.processing = false; this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); this.audioService.playSound('ascend-chime-cartoon'); @@ -870,7 +870,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { tokenResult.details.cashAppPay.referenceId, costUSD ).subscribe({ - next: () => { + next: (response) => { + this.accelerationResponse = response; this.processing = false; this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); this.audioService.playSound('ascend-chime-cartoon'); @@ -936,7 +937,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { this.loadingBtcpayInvoice = true; this.servicesApiService.generateBTCPayAcceleratorInvoice$(this.tx.txid, this.userBid).pipe( switchMap(response => { - return this.servicesApiService.retreiveInvoice$(response.btcpayInvoiceId); + return this.servicesApiService.retrieveInvoice$(response.btcpayInvoiceId); }), catchError(error => { console.log(error); diff --git a/frontend/src/app/components/address-graph/address-graph.component.ts b/frontend/src/app/components/address-graph/address-graph.component.ts index 2bbfd5e34..005c64e9f 100644 --- a/frontend/src/app/components/address-graph/address-graph.component.ts +++ b/frontend/src/app/components/address-graph/address-graph.component.ts @@ -44,6 +44,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { @Input() right: number | string = 10; @Input() left: number | string = 70; @Input() widget: boolean = false; + @Input() label: string = ''; @Input() defaultFiat: boolean = false; @Input() showLegend: boolean = true; @Input() showYAxis: boolean = true; @@ -55,6 +56,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { hoverData: any[] = []; conversions: any; allowZoom: boolean = false; + labelGraphic: any; selected = { [$localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`]: true, 'Fiat': false }; @@ -85,6 +87,18 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { ngOnChanges(changes: SimpleChanges): void { this.isLoading = true; + this.labelGraphic = this.label ? { + type: 'text', + right: '36px', + bottom: '36px', + z: 100, + silent: true, + style: { + fill: '#fff', + text: this.label, + font: '24px sans-serif' + } + } : undefined; if (!this.addressSummary$ && (!this.address || !this.stats)) { return; } @@ -205,6 +219,10 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { right: this.adjustedRight, left: this.adjustedLeft, }, + graphic: this.labelGraphic ? [{ + ...this.labelGraphic, + right: this.adjustedRight + 22 + 'px', + }] : undefined, legend: (this.showLegend && !this.stateService.isAnyTestnet()) ? { data: [ { @@ -443,6 +461,10 @@ export class AddressGraphComponent implements OnChanges, OnDestroy { right: this.adjustedRight, left: this.adjustedLeft, }, + graphic: this.labelGraphic ? [{ + ...this.labelGraphic, + right: this.adjustedRight + 22 + 'px', + }] : undefined, legend: { selected: this.selected, }, diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html index 8ca1a5ac4..defb7c068 100644 --- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html +++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html @@ -238,7 +238,7 @@   - + @@ -272,7 +272,7 @@   - + diff --git a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html index 7e39d9341..cd016471b 100644 --- a/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html +++ b/frontend/src/app/components/liquid-master-page/liquid-master-page.component.html @@ -70,11 +70,6 @@ - diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.component.html b/frontend/src/app/components/privacy-policy/privacy-policy.component.html index 06b09ad30..bfd6159c4 100644 --- a/frontend/src/app/components/privacy-policy/privacy-policy.component.html +++ b/frontend/src/app/components/privacy-policy/privacy-policy.component.html @@ -45,14 +45,14 @@
-

USING MEMPOOL ACCELERATOR™

+

USING MEMPOOL ACCELERATOR®

-

If you use Mempool Accelerator™ your acceleration request will be sent to us and relayed to Mempool's mining pool partners. We will store the TXID of the transactions you accelerate with us. We share this information with our mining pool partners, and publicly display accelerated transaction details on our website and APIs. No personal information or account identifiers will be shared with any third party including mining pool partners.

+

If you use Mempool Accelerator® your acceleration request will be sent to us and relayed to Mempool's mining pool partners. We will store the TXID of the transactions you accelerate with us. We share this information with our mining pool partners, and publicly display accelerated transaction details on our website and APIs. No personal information or account identifiers will be shared with any third party including mining pool partners.

-

When using Mempool Accelerator™ the mempool.space privacy policy will apply: https://mempool.space/privacy-policy.

+

When using Mempool Accelerator® the mempool.space privacy policy will apply: https://mempool.space/privacy-policy.


- +

SIGNING UP FOR AN ACCOUNT ON MEMPOOL.SPACE

@@ -67,7 +67,7 @@
  • If you sign up for a subscription to Mempool Enterprise™ we also collect your company name which is not shared with any third-party.
  • -
  • If you sign up for an account on mempool.space and use Mempool Accelerator™ Pro your accelerated transactions will be associated with your account for the purposes of accounting.
  • +
  • If you sign up for an account on mempool.space and use Mempool Accelerator® Pro your accelerated transactions will be associated with your account for the purposes of accounting.
  • @@ -101,7 +101,7 @@

    We aim to retain your data only as long as necessary:

      -
    • An account is considered inactive if all of the following conditions are met: a) No login activity within the past 6 months, b) No active subscriptions associated with the account, c) No Mempool Accelerator™ Pro account credit
    • +
    • An account is considered inactive if all of the following conditions are met: a) No login activity within the past 6 months, b) No active subscriptions associated with the account, c) No Mempool Accelerator® Pro account credit
    • If an account meets the criteria for inactivity as defined above, we will automatically delete the associated account data after a period of 6 months of continuous inactivity, except in the case of payment disputes or account irregularities.
    diff --git a/frontend/src/app/components/statistics/statistics.component.html b/frontend/src/app/components/statistics/statistics.component.html index 168a3c0c3..eb37cc858 100644 --- a/frontend/src/app/components/statistics/statistics.component.html +++ b/frontend/src/app/components/statistics/statistics.component.html @@ -16,9 +16,6 @@ - - -
    diff --git a/frontend/src/app/components/svg-images/svg-images.component.html b/frontend/src/app/components/svg-images/svg-images.component.html index 76aa3de85..04b99dea6 100644 --- a/frontend/src/app/components/svg-images/svg-images.component.html +++ b/frontend/src/app/components/svg-images/svg-images.component.html @@ -137,7 +137,7 @@ - Mempool Accelerator™ + Mempool Accelerator® @@ -695,4 +695,4 @@ - \ No newline at end of file + diff --git a/frontend/src/app/components/television/television.component.html b/frontend/src/app/components/television/television.component.html deleted file mode 100644 index 23dd18389..000000000 --- a/frontend/src/app/components/television/television.component.html +++ /dev/null @@ -1,25 +0,0 @@ -
    -
    -
    - -
    -
    -
    - -
    - - -
    -
    -
    -
    -
    -
    -
    diff --git a/frontend/src/app/components/television/television.component.scss b/frontend/src/app/components/television/television.component.scss deleted file mode 100644 index 9a6cbcc24..000000000 --- a/frontend/src/app/components/television/television.component.scss +++ /dev/null @@ -1,80 +0,0 @@ - -.loading { - margin: auto; - width: 100%; - display: flex; - text-align: center; - justify-content: center; - height: 100vh; - align-items: center; -} - -#tv-wrapper { - height: 100vh; - overflow: hidden; - position: relative; -} - -.chart-holder { - position: relative; - height: 655px; - width: 100%; - margin: 30px auto 0; -} - -.blockchain-wrapper { - display: block; - height: 100%; - min-height: 240px; - position: relative; - top: 30px; - - .position-container { - position: absolute; - left: 0; - bottom: 170px; - transform: translateX(50vw); - } - - #divider { - width: 2px; - height: 175px; - left: 0; - top: -40px; - position: absolute; - img { - position: absolute; - left: -100px; - top: -28px; - } - } - - &.time-ltr { - .blocks-wrapper { - transform: scaleX(-1); - } - } -} - -:host-context(.ltr-layout) { - .blockchain-wrapper.time-ltr .blocks-wrapper, - .blockchain-wrapper .blocks-wrapper { - direction: ltr; - } -} - -:host-context(.rtl-layout) { - .blockchain-wrapper.time-ltr .blocks-wrapper, - .blockchain-wrapper .blocks-wrapper { - direction: rtl; - } -} - -.tv-container { - display: flex; - margin-top: 0px; - flex-direction: column; -} - - - diff --git a/frontend/src/app/components/television/television.component.ts b/frontend/src/app/components/television/television.component.ts deleted file mode 100644 index 1507f3d97..000000000 --- a/frontend/src/app/components/television/television.component.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { WebsocketService } from '@app/services/websocket.service'; -import { OptimizedMempoolStats } from '@interfaces/node-api.interface'; -import { StateService } from '@app/services/state.service'; -import { ApiService } from '@app/services/api.service'; -import { SeoService } from '@app/services/seo.service'; -import { ActivatedRoute } from '@angular/router'; -import { map, scan, startWith, switchMap, tap } from 'rxjs/operators'; -import { interval, merge, Observable, Subscription } from 'rxjs'; -import { ChangeDetectionStrategy } from '@angular/core'; - -@Component({ - selector: 'app-television', - templateUrl: './television.component.html', - styleUrls: ['./television.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class TelevisionComponent implements OnInit, OnDestroy { - - mempoolStats: OptimizedMempoolStats[] = []; - statsSubscription$: Observable; - fragment: string; - timeLtrSubscription: Subscription; - timeLtr: boolean = this.stateService.timeLtr.value; - - constructor( - private websocketService: WebsocketService, - private apiService: ApiService, - private stateService: StateService, - private seoService: SeoService, - private route: ActivatedRoute - ) { } - - refreshStats(time: number, fn: Observable) { - return interval(time).pipe(startWith(0), switchMap(() => fn)); - } - - ngOnInit() { - this.seoService.setTitle($localize`:@@46ce8155c9ab953edeec97e8950b5a21e67d7c4e:TV view`); - this.seoService.setDescription($localize`:@@meta.description.tv:See Bitcoin blocks and mempool congestion in real-time in a simplified format perfect for a TV.`); - this.websocketService.want(['blocks', 'live-2h-chart', 'mempool-blocks']); - - this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { - this.timeLtr = !!ltr; - }); - - this.statsSubscription$ = merge( - this.stateService.live2Chart$.pipe(map(stats => [stats])), - this.route.fragment - .pipe( - tap(fragment => { this.fragment = fragment ?? '2h'; }), - switchMap((fragment) => { - const minute = 60000; const hour = 3600000; - switch (fragment) { - case '24h': return this.apiService.list24HStatistics$(); - case '1w': return this.refreshStats(5 * minute, this.apiService.list1WStatistics$()); - case '1m': return this.refreshStats(30 * minute, this.apiService.list1MStatistics$()); - case '3m': return this.refreshStats(2 * hour, this.apiService.list3MStatistics$()); - case '6m': return this.refreshStats(3 * hour, this.apiService.list6MStatistics$()); - case '1y': return this.refreshStats(8 * hour, this.apiService.list1YStatistics$()); - case '2y': return this.refreshStats(8 * hour, this.apiService.list2YStatistics$()); - case '3y': return this.refreshStats(12 * hour, this.apiService.list3YStatistics$()); - default /* 2h */: return this.apiService.list2HStatistics$(); - } - }) - ) - ) - .pipe( - scan((mempoolStats, newStats) => { - if (newStats.length > 1) { - mempoolStats = newStats; - } else if (['2h', '24h'].includes(this.fragment)) { - mempoolStats.unshift(newStats[0]); - const now = Date.now() / 1000; - const start = now - (this.fragment === '2h' ? (2 * 60 * 60) : (24 * 60 * 60) ); - mempoolStats = mempoolStats.filter(p => p.added >= start); - } - return mempoolStats; - }) - ); - } - - ngOnDestroy() { - this.timeLtrSubscription.unsubscribe(); - } -} diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.component.html b/frontend/src/app/components/terms-of-service/terms-of-service.component.html index 709605a9f..51f035436 100644 --- a/frontend/src/app/components/terms-of-service/terms-of-service.component.html +++ b/frontend/src/app/components/terms-of-service/terms-of-service.component.html @@ -67,9 +67,9 @@
    -

    MEMPOOL ACCELERATOR™

    +

    MEMPOOL ACCELERATOR®

    -

    Mempool Accelerator™ enables members of the Bitcoin community to submit requests for transaction prioritization.

    +

    Mempool Accelerator® enables members of the Bitcoin community to submit requests for transaction prioritization.

    • Mempool will use reasonable commercial efforts to relay user acceleration requests to Mempool's mining pool partners, but it is at the discretion of Mempool and Mempool's mining pool partners to accept acceleration requests.
    • @@ -84,11 +84,11 @@
      -
    • All acceleration payments and Mempool Accelerator™ account credit top-ups are non-refundable.
    • +
    • All acceleration payments and Mempool Accelerator® account credit top-ups are non-refundable.

    • -
    • Mempool Accelerator™ account credit top-ups are prepayment for future accelerations and cannot be withdrawn or transferred.
    • +
    • Mempool Accelerator® account credit top-ups are prepayment for future accelerations and cannot be withdrawn or transferred.

    • diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.component.html b/frontend/src/app/components/trademark-policy/trademark-policy.component.html index e12cbb8b2..ff4fd1cb0 100644 --- a/frontend/src/app/components/trademark-policy/trademark-policy.component.html +++ b/frontend/src/app/components/trademark-policy/trademark-policy.component.html @@ -8,7 +8,7 @@

      Trademark Policy and Guidelines

      The Mempool Open Source Project ®
      -
      Updated: August 19, 2024
      +
      Updated: February 11, 2025

      @@ -59,6 +59,7 @@ Mempool Accelerator Mempool Enterprise Mempool Liquidity + Mempool mempool.space Be your own explorer Explore the full Bitcoin ecosystem @@ -340,7 +341,8 @@

      Also, if you are using our Marks in a way described in the sections "Uses for Which We Are Granting a License," you must include the following trademark attribution at the foot of the webpage where you have used the Mark (or, if in a book, on the credits page), on any packaging or labeling, and on advertising or marketing materials:

      -

      "The Mempool Open Source Project®, Mempool Accelerator™, Mempool Enterprise®, Mempool Liquidity™, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."

      +

      "The Mempool Open Source Project®, Mempool Accelerator®, Mempool Enterprise®, Mempool Liquidity™, Mempool®, mempool.space®, Be your own explorer™, Explore the full Bitcoin ecosystem®, Mempool Goggles™, the mempool logo;, the mempool Square logo;, the mempool Blocks logo;, the mempool Blocks 3 | 2 logo;, the mempool.space Vertical Logo;, the Mempool Accelerator logo;, the Mempool Goggles logo;, and the mempool.space Horizontal logo are either registered trademarks or trademarks of Mempool Space K.K in Japan, the United States, and/or other countries, and are used with permission. Mempool Space K.K. has no affiliation with and does not sponsor or endorse the information provided herein."

      +
    • What to Do When You See Abuse

    • diff --git a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html index 78bba955c..819e27d89 100644 --- a/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html +++ b/frontend/src/app/components/transaction/transaction-details/transaction-details.component.html @@ -170,7 +170,7 @@ @if (!tx?.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !showAccelerationSummary && notAcceleratedOnLoad) { @@ -318,4 +318,4 @@ - \ No newline at end of file + diff --git a/frontend/src/app/components/transaction/transaction-raw.component.html b/frontend/src/app/components/transaction/transaction-raw.component.html index 3bd8ee6d2..35701889b 100644 --- a/frontend/src/app/components/transaction/transaction-raw.component.html +++ b/frontend/src/app/components/transaction/transaction-raw.component.html @@ -30,7 +30,6 @@
      -
      @@ -40,14 +39,18 @@
      -
      +
      - + This transaction is stored locally in your browser. Broadcast it to add it to the mempool. + + Redirecting to transaction page... + - + +
      @if (!hasPrevouts) { diff --git a/frontend/src/app/components/transaction/transaction-raw.component.scss b/frontend/src/app/components/transaction/transaction-raw.component.scss index 5bbe5601e..a4b386cee 100644 --- a/frontend/src/app/components/transaction/transaction-raw.component.scss +++ b/frontend/src/app/components/transaction/transaction-raw.component.scss @@ -191,4 +191,12 @@ .no-cursor { cursor: default !important; pointer-events: none; +} + +.btn-broadcast { + margin-left: 5px; + @media (max-width: 567px) { + margin-left: 0; + margin-top: 5px; + } } \ No newline at end of file diff --git a/frontend/src/app/components/transaction/transaction-raw.component.ts b/frontend/src/app/components/transaction/transaction-raw.component.ts index 5ce170e12..2e4dd4868 100644 --- a/frontend/src/app/components/transaction/transaction-raw.component.ts +++ b/frontend/src/app/components/transaction/transaction-raw.component.ts @@ -3,7 +3,7 @@ import { Transaction, Vout } from '@interfaces/electrs.interface'; import { StateService } from '../../services/state.service'; import { Filter, toFilters } from '../../shared/filters.utils'; import { decodeRawTransaction, getTransactionFlags, addInnerScriptsToVin, countSigops } from '../../shared/transaction.utils'; -import { firstValueFrom, Subscription } from 'rxjs'; +import { catchError, firstValueFrom, Subscription, switchMap, tap, throwError, timer } from 'rxjs'; import { WebsocketService } from '../../services/websocket.service'; import { ActivatedRoute, Router } from '@angular/router'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; @@ -36,6 +36,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy { isLoadingBroadcast: boolean; errorBroadcast: string; successBroadcast: boolean; + broadcastSubscription: Subscription; isMobile: boolean; @ViewChild('graphContainer') @@ -82,7 +83,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy { this.resetState(); this.isLoading = true; try { - const { tx, hex } = decodeRawTransaction(this.pushTxForm.get('txRaw').value, this.stateService.network); + const { tx, hex } = decodeRawTransaction(this.pushTxForm.get('txRaw').value.trim(), this.stateService.network); await this.fetchPrevouts(tx); await this.fetchCpfpInfo(tx); this.processTransaction(tx, hex); @@ -207,18 +208,22 @@ export class TransactionRawComponent implements OnInit, OnDestroy { }); } - async postTx(): Promise { + postTx(): void { this.isLoadingBroadcast = true; this.errorBroadcast = null; - return new Promise((resolve, reject) => { - this.apiService.postTransaction$(this.rawHexTransaction) - .subscribe((result) => { + + this.broadcastSubscription = this.apiService.postTransaction$(this.rawHexTransaction).pipe( + tap((txid: string) => { this.isLoadingBroadcast = false; this.successBroadcast = true; - this.transaction.txid = result; - resolve(result); - }, - (error) => { + this.transaction.txid = txid; + }), + switchMap((txid: string) => + timer(2000).pipe( + tap(() => this.router.navigate([this.relativeUrlPipe.transform('/tx/' + txid)])), + ) + ), + catchError((error) => { if (typeof error.error === 'string') { const matchText = error.error.replace(/\\/g, '').match('"message":"(.*?)"'); this.errorBroadcast = 'Failed to broadcast transaction, reason: ' + (matchText && matchText[1] || error.error); @@ -226,9 +231,9 @@ export class TransactionRawComponent implements OnInit, OnDestroy { this.errorBroadcast = 'Failed to broadcast transaction, reason: ' + error.message; } this.isLoadingBroadcast = false; - reject(this.error); - }); - }); + return throwError(() => error); + }) + ).subscribe(); } resetState() { @@ -253,6 +258,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy { this.missingPrevouts = []; this.stateService.markBlock$.next({}); this.mempoolBlocksSubscription?.unsubscribe(); + this.broadcastSubscription?.unsubscribe(); } resetForm() { @@ -308,6 +314,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy { this.mempoolBlocksSubscription?.unsubscribe(); this.flowPrefSubscription?.unsubscribe(); this.stateService.markBlock$.next({}); + this.broadcastSubscription?.unsubscribe(); } } diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 6f1d76538..7721298fb 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -16,6 +16,11 @@
      {{ errorUnblinded }}
      + @if (similarityMatches.get(tx.txid)?.size) { + + }
      @@ -68,9 +73,11 @@ - - - + {{ vin.prevout.scriptpubkey_type?.toUpperCase() }} @@ -217,9 +224,11 @@ 'highlight': this.addresses.length && ((vout.scriptpubkey_type !== 'p2pk' && addresses.includes(vout.scriptpubkey_address)) || this.addresses.includes(vout.scriptpubkey.slice(2, -2))) }">
      - - - + P2PK diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 20f11abd8..6f01a6e59 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -14,6 +14,7 @@ import { StorageService } from '@app/services/storage.service'; import { OrdApiService } from '@app/services/ord-api.service'; import { Inscription } from '@app/shared/ord/inscription.utils'; import { Etching, Runestone } from '@app/shared/ord/rune.utils'; +import { ADDRESS_SIMILARITY_THRESHOLD, AddressMatch, AddressSimilarity, AddressType, AddressTypeInfo, checkedCompareAddressStrings, detectAddressType } from '@app/shared/address-utils'; @Component({ selector: 'app-transactions-list', @@ -55,6 +56,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { showFullScript: { [vinIndex: number]: boolean } = {}; showFullWitness: { [vinIndex: number]: { [witnessIndex: number]: boolean } } = {}; showOrdData: { [key: string]: { show: boolean; inscriptions?: Inscription[]; runestone?: Runestone, runeInfo?: { [id: string]: { etching: Etching; txid: string; } }; } } = {}; + similarityMatches: Map> = new Map(); constructor( public stateService: StateService, @@ -144,6 +146,8 @@ export class TransactionsListComponent implements OnInit, OnChanges { this.currency = currency; this.refreshPrice(); }); + + this.updateAddressSimilarities(); } refreshPrice(): void { @@ -183,6 +187,8 @@ export class TransactionsListComponent implements OnInit, OnChanges { } } if (changes.transactions || changes.addresses) { + this.similarityMatches.clear(); + this.updateAddressSimilarities(); if (!this.transactions || !this.transactions.length) { return; } @@ -296,6 +302,56 @@ export class TransactionsListComponent implements OnInit, OnChanges { } } + updateAddressSimilarities(): void { + if (!this.transactions || !this.transactions.length) { + return; + } + for (const tx of this.transactions) { + if (this.similarityMatches.get(tx.txid)) { + continue; + } + + const similarityGroups: Map = new Map(); + let lastGroup = 0; + + // Check for address poisoning similarity matches + this.similarityMatches.set(tx.txid, new Map()); + const comparableVouts = [ + ...tx.vout.slice(0, 20), + ...this.addresses.map(addr => ({ scriptpubkey_address: addr, scriptpubkey_type: detectAddressType(addr, this.stateService.network) })) + ].filter(v => ['p2pkh', 'p2sh', 'v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(v.scriptpubkey_type)); + const comparableVins = tx.vin.slice(0, 20).map(v => v.prevout).filter(v => ['p2pkh', 'p2sh', 'v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(v?.scriptpubkey_type)); + for (const vout of comparableVouts) { + const address = vout.scriptpubkey_address; + const addressType = vout.scriptpubkey_type; + if (this.similarityMatches.get(tx.txid)?.has(address)) { + continue; + } + for (const compareAddr of [ + ...comparableVouts.filter(v => v.scriptpubkey_type === addressType && v.scriptpubkey_address !== address), + ...comparableVins.filter(v => v.scriptpubkey_type === addressType && v.scriptpubkey_address !== address) + ]) { + const similarity = checkedCompareAddressStrings(address, compareAddr.scriptpubkey_address, addressType as AddressType, this.stateService.network); + if (similarity?.status === 'comparable' && similarity.score > ADDRESS_SIMILARITY_THRESHOLD) { + let group = similarityGroups.get(address) || lastGroup++; + similarityGroups.set(address, group); + const bestVout = this.similarityMatches.get(tx.txid)?.get(address); + if (!bestVout || bestVout.score < similarity.score) { + this.similarityMatches.get(tx.txid)?.set(address, { score: similarity.score, match: similarity.left, group }); + } + // opportunistically update the entry for the compared address + const bestCompare = this.similarityMatches.get(tx.txid)?.get(compareAddr.scriptpubkey_address); + if (!bestCompare || bestCompare.score < similarity.score) { + group = similarityGroups.get(compareAddr.scriptpubkey_address) || lastGroup++; + similarityGroups.set(compareAddr.scriptpubkey_address, group); + this.similarityMatches.get(tx.txid)?.set(compareAddr.scriptpubkey_address, { score: similarity.score, match: similarity.right, group }); + } + } + } + } + } + } + onScroll(): void { this.loadMore.emit(); } diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index c32baa3f7..5e9608bdf 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -11803,7 +11803,7 @@ export const restApiDocsData = [ fragment: "accelerator-cancel", title: "POST Cancel Acceleration (Pro)", description: { - default: "

      Sends a request to cancel an acceleration in the accelerating status.
      You can retreive eligible acceleration id using the history endpoint GET /api/v1/services/accelerator/history?status=accelerating." + default: "

      Sends a request to cancel an acceleration in the accelerating status.
      You can retrieve eligible acceleration id using the history endpoint GET /api/v1/services/accelerator/history?status=accelerating." }, urlString: "/v1/services/accelerator/cancel", showConditions: [""], diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index 75e37a3bd..d359500a0 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -219,7 +219,7 @@ -

      To get your transaction confirmed quicker, you will need to increase its effective feerate.

      If your transaction was created with RBF enabled, your stuck transaction can simply be replaced with a new one that has a higher fee. Otherwise, if you control any of the stuck transaction's outputs, you can use CPFP to increase your stuck transaction's effective feerate.

      If you are not sure how to do RBF or CPFP, work with the tool you used to make the transaction (wallet software, exchange company, etc).

      Another option to get your transaction confirmed more quickly is Mempool Accelerator™.

      +

      To get your transaction confirmed quicker, you will need to increase its effective feerate.

      If your transaction was created with RBF enabled, your stuck transaction can simply be replaced with a new one that has a higher fee. Otherwise, if you control any of the stuck transaction's outputs, you can use CPFP to increase your stuck transaction's effective feerate.

      If you are not sure how to do RBF or CPFP, work with the tool you used to make the transaction (wallet software, exchange company, etc).

      Another option to get your transaction confirmed more quickly is Mempool Accelerator®.

      diff --git a/frontend/src/app/graphs/echarts.ts b/frontend/src/app/graphs/echarts.ts index 67ed7e3b8..36a0517e4 100644 --- a/frontend/src/app/graphs/echarts.ts +++ b/frontend/src/app/graphs/echarts.ts @@ -1,7 +1,7 @@ // Import tree-shakeable echarts import * as echarts from 'echarts/core'; import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart, CustomChart } from 'echarts/charts'; -import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent } from 'echarts/components'; +import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent, GraphicComponent } from 'echarts/components'; import { SVGRenderer, CanvasRenderer } from 'echarts/renderers'; // Typescript interfaces import { EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption } from 'echarts'; @@ -13,6 +13,6 @@ echarts.use([ LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent, LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart, GaugeChart, - CustomChart, + CustomChart, GraphicComponent ]); export { echarts, EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption }; \ No newline at end of file diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts index f882b4221..8ebf06f7c 100644 --- a/frontend/src/app/graphs/graphs.module.ts +++ b/frontend/src/app/graphs/graphs.module.ts @@ -26,7 +26,6 @@ import { StatisticsComponent } from '@components/statistics/statistics.component import { MempoolBlockComponent } from '@components/mempool-block/mempool-block.component'; import { PoolRankingComponent } from '@components/pool-ranking/pool-ranking.component'; import { PoolComponent } from '@components/pool/pool.component'; -import { TelevisionComponent } from '@components/television/television.component'; import { DashboardComponent } from '@app/dashboard/dashboard.component'; import { CustomDashboardComponent } from '@components/custom-dashboard/custom-dashboard.component'; import { MiningDashboardComponent } from '@components/mining-dashboard/mining-dashboard.component'; @@ -56,7 +55,6 @@ import { CommonModule } from '@angular/common'; AcceleratorDashboardComponent, PoolComponent, PoolRankingComponent, - TelevisionComponent, StatisticsComponent, GraphsComponent, diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index 886d55072..e8dbaece3 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -16,7 +16,6 @@ import { PoolRankingComponent } from '@components/pool-ranking/pool-ranking.comp import { PoolComponent } from '@components/pool/pool.component'; import { StartComponent } from '@components/start/start.component'; import { StatisticsComponent } from '@components/statistics/statistics.component'; -import { TelevisionComponent } from '@components/television/television.component'; import { DashboardComponent } from '@app/dashboard/dashboard.component'; import { CustomDashboardComponent } from '@components/custom-dashboard/custom-dashboard.component'; import { AccelerationFeesGraphComponent } from '@components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; @@ -180,11 +179,6 @@ const routes: Routes = [ }, ] }, - { - path: 'tv', - data: { networks: ['bitcoin', 'liquid'] }, - component: TelevisionComponent - }, ]; @NgModule({ diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts index 59dc92358..a9550e731 100644 --- a/frontend/src/app/services/services-api.service.ts +++ b/frontend/src/app/services/services-api.service.ts @@ -213,7 +213,7 @@ export class ServicesApiServices { return this.httpClient.post(`${this.stateService.env.SERVICES_API}/payments/bitcoin`, params); } - retreiveInvoice$(invoiceId: string): Observable { + retrieveInvoice$(invoiceId: string): Observable { return this.httpClient.get(`${this.stateService.env.SERVICES_API}/payments/bitcoin/invoice?id=${invoiceId}`); } diff --git a/frontend/src/app/shared/address-utils.ts b/frontend/src/app/shared/address-utils.ts index 0a7f2df02..0ca4ec2e0 100644 --- a/frontend/src/app/shared/address-utils.ts +++ b/frontend/src/app/shared/address-utils.ts @@ -78,6 +78,7 @@ const p2trRegex = RegExp('^p' + BECH32_CHARS_LW + '{58}$'); const pubkeyRegex = RegExp('^' + `(04${HEX_CHARS}{128})|(0[23]${HEX_CHARS}{64})$`); export function detectAddressType(address: string, network: string): AddressType { + network = network || 'mainnet'; // normal address types const firstChar = address.substring(0, 1); if (ADDRESS_PREFIXES[network].base58.pubkey.includes(firstChar) && base58Regex.test(address.slice(1))) { @@ -211,6 +212,18 @@ export class AddressTypeInfo { } } + public compareTo(other: AddressTypeInfo): AddressSimilarityResult { + return compareAddresses(this.address, other.address, this.network); + } + + public compareToString(other: string): AddressSimilarityResult { + if (other === this.address) { + return { status: 'identical' }; + } + const otherInfo = new AddressTypeInfo(this.network, other); + return this.compareTo(otherInfo); + } + private processScript(script: ScriptInfo): void { this.scripts.set(script.key, script); if (script.template?.type === 'multisig') { @@ -218,3 +231,135 @@ export class AddressTypeInfo { } } } + +export interface AddressMatch { + prefix: string; + postfix: string; +} + +export interface AddressSimilarity { + status: 'comparable'; + score: number; + left: AddressMatch; + right: AddressMatch; +} +export type AddressSimilarityResult = + | { status: 'identical' } + | { status: 'incomparable' } + | AddressSimilarity; + +export const ADDRESS_SIMILARITY_THRESHOLD = 10_000_000; // 1 false positive per ~10 million comparisons + +function fuzzyPrefixMatch(a: string, b: string, rtl: boolean = false): { score: number, matchA: string, matchB: string } { + let score = 0; + let gap = false; + let done = false; + + let ai = 0; + let bi = 0; + let prefixA = ''; + let prefixB = ''; + if (rtl) { + a = a.split('').reverse().join(''); + b = b.split('').reverse().join(''); + } + + while (ai < a.length && bi < b.length && !done) { + if (a[ai] === b[bi]) { + // matching characters + prefixA += a[ai]; + prefixB += b[bi]; + score++; + ai++; + bi++; + } else if (!gap) { + // try looking ahead in both strings to find the best match + const nextMatchA = (ai + 1 < a.length && a[ai + 1] === b[bi]); + const nextMatchB = (bi + 1 < b.length && a[ai] === b[bi + 1]); + const nextMatchBoth = (ai + 1 < a.length && bi + 1 < b.length && a[ai + 1] === b[bi + 1]); + if (nextMatchBoth) { + // single differing character + prefixA += a[ai]; + prefixB += b[bi]; + ai++; + bi++; + } else if (nextMatchA) { + // character missing in b + prefixA += a[ai]; + ai++; + } else if (nextMatchB) { + // character missing in a + prefixB += b[bi]; + bi++; + } else { + ai++; + bi++; + } + gap = true; + } else { + done = true; + } + } + + if (rtl) { + prefixA = prefixA.split('').reverse().join(''); + prefixB = prefixB.split('').reverse().join(''); + } + + return { score, matchA: prefixA, matchB: prefixB }; +} + +export function compareAddressInfo(a: AddressTypeInfo, b: AddressTypeInfo): AddressSimilarityResult { + if (a.address === b.address) { + return { status: 'identical' }; + } + if (a.type !== b.type) { + return { status: 'incomparable' }; + } + if (!['p2pkh', 'p2sh', 'p2sh-p2wpkh', 'p2sh-p2wsh', 'v0_p2wpkh', 'v0_p2wsh', 'v1_p2tr'].includes(a.type)) { + return { status: 'incomparable' }; + } + const isBase58 = a.type === 'p2pkh' || a.type === 'p2sh'; + + const left = fuzzyPrefixMatch(a.address, b.address); + const right = fuzzyPrefixMatch(a.address, b.address, true); + // depending on address type, some number of matching prefix characters are guaranteed + const prefixScore = isBase58 ? 1 : ADDRESS_PREFIXES[a.network || 'mainnet'].bech32.length; + + // add the two scores together + const totalScore = left.score + right.score - prefixScore; + + // adjust for the size of the alphabet (58 vs 32) + const normalizedScore = Math.pow(isBase58 ? 58 : 32, totalScore); + + return { + status: 'comparable', + score: normalizedScore, + left: { + prefix: left.matchA, + postfix: right.matchA, + }, + right: { + prefix: left.matchB, + postfix: right.matchB, + }, + }; +} + +export function compareAddresses(a: string, b: string, network: string): AddressSimilarityResult { + if (a === b) { + return { status: 'identical' }; + } + const aInfo = new AddressTypeInfo(network, a); + return aInfo.compareToString(b); +} + +// avoids the overhead of creating AddressTypeInfo objects for each address, +// but a and b *MUST* be valid normalized addresses, of the same valid type +export function checkedCompareAddressStrings(a: string, b: string, type: AddressType, network: string): AddressSimilarityResult { + return compareAddressInfo( + { address: a, type: type, network: network } as AddressTypeInfo, + { address: b, type: type, network: network } as AddressTypeInfo, + ); +} + diff --git a/frontend/src/app/shared/components/address-text/address-text.component.html b/frontend/src/app/shared/components/address-text/address-text.component.html new file mode 100644 index 000000000..ddcd8d751 --- /dev/null +++ b/frontend/src/app/shared/components/address-text/address-text.component.html @@ -0,0 +1,17 @@ + +@if (similarity) { + +} @else { + + + +} \ No newline at end of file diff --git a/frontend/src/app/shared/components/address-text/address-text.component.scss b/frontend/src/app/shared/components/address-text/address-text.component.scss new file mode 100644 index 000000000..3b5bf50b7 --- /dev/null +++ b/frontend/src/app/shared/components/address-text/address-text.component.scss @@ -0,0 +1,32 @@ +.address-text { + text-overflow: unset; + display: flex; + flex-direction: row; + align-items: start; + position: relative; + + font-family: monospace; + + .infix { + flex-grow: 0; + flex-shrink: 1; + overflow: hidden; + text-overflow: ellipsis; + user-select: none; + + text-decoration: underline 2px; + } + + .prefix, .postfix { + flex-shrink: 0; + flex-grow: 0; + user-select: none; + + text-decoration: underline var(--red) 2px; + } +} + +.poison-alert { + margin-left: .5em; + color: var(--yellow); +} \ No newline at end of file diff --git a/frontend/src/app/shared/components/address-text/address-text.component.ts b/frontend/src/app/shared/components/address-text/address-text.component.ts new file mode 100644 index 000000000..f618428aa --- /dev/null +++ b/frontend/src/app/shared/components/address-text/address-text.component.ts @@ -0,0 +1,20 @@ +import { Component, Input } from '@angular/core'; +import { AddressMatch, AddressTypeInfo } from '@app/shared/address-utils'; + +@Component({ + selector: 'app-address-text', + templateUrl: './address-text.component.html', + styleUrls: ['./address-text.component.scss'] +}) +export class AddressTextComponent { + @Input() address: string; + @Input() info: AddressTypeInfo | null; + @Input() similarity: { score: number, match: AddressMatch, group: number } | null; + + groupColors: string[] = [ + 'var(--primary)', + 'var(--success)', + 'var(--info)', + 'white', + ]; +} diff --git a/frontend/src/app/shared/script.utils.ts b/frontend/src/app/shared/script.utils.ts index 62a7a5845..8453bdd63 100644 --- a/frontend/src/app/shared/script.utils.ts +++ b/frontend/src/app/shared/script.utils.ts @@ -256,6 +256,11 @@ export function detectScriptTemplate(type: ScriptType, script_asm: string, witne return ScriptTemplates.multisig(tapscriptMultisig.m, tapscriptMultisig.n); } + const tapscriptUnanimousMultisig = parseTapscriptUnanimousMultisig(script_asm); + if (tapscriptUnanimousMultisig) { + return ScriptTemplates.multisig(tapscriptUnanimousMultisig, tapscriptUnanimousMultisig); + } + return; } @@ -310,11 +315,13 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number, } const ops = script.split(' '); - // At minimum, one pubkey group (3 tokens) + m push + final opcode = 5 tokens - if (ops.length < 5) return; + // At minimum, 2 pubkey group (3 tokens) + m push + final opcode = 8 tokens + if (ops.length < 8) { + return; + } const finalOp = ops.pop(); - if (finalOp !== 'OP_NUMEQUAL' && finalOp !== 'OP_GREATERTHANOREQUAL') { + if (!['OP_NUMEQUAL', 'OP_NUMEQUALVERIFY', 'OP_GREATERTHANOREQUAL', 'OP_GREATERTHAN', 'OP_EQUAL', 'OP_EQUALVERIFY'].includes(finalOp)) { return; } @@ -329,6 +336,10 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number, return; } + if (finalOp === 'OP_GREATERTHAN') { + m += 1; + } + if (ops.length % 3 !== 0) { return; } @@ -360,6 +371,53 @@ export function parseTapscriptMultisig(script: string): undefined | { m: number, return { m, n }; } +export function parseTapscriptUnanimousMultisig(script: string): undefined | number { + if (!script) { + return; + } + + const ops = script.split(' '); + // At minimum, 2 pubkey group (3 tokens) = 6 tokens + if (ops.length < 6) { + return; + } + + if (ops.length % 3 !== 0) { + return; + } + + const n = ops.length / 3; + + for (let i = 0; i < n; i++) { + const pushOp = ops.shift(); + const pubkey = ops.shift(); + const sigOp = ops.shift(); + + if (pushOp !== 'OP_PUSHBYTES_32') { + return; + } + if (!/^[0-9a-fA-F]{64}$/.test(pubkey)) { + return; + } + if (i < n - 1) { + if (sigOp !== 'OP_CHECKSIGVERIFY') { + return; + } + } else { + // Last opcode can be either CHECKSIG or CHECKSIGVERIFY + if (!(sigOp === 'OP_CHECKSIGVERIFY' || sigOp === 'OP_CHECKSIG')) { + return; + } + } + } + + if (ops.length) { + return; + } + + return n; +} + export function getVarIntLength(n: number): number { if (n < 0xfd) { return 1; diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index d937e6bbb..c3f8375c8 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -3,11 +3,11 @@ import { CommonModule } from '@angular/common'; import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; 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, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, - faCircleXmark, faCalendarCheck, faMoneyBillTrendUp, faRobot, faShareNodes, faCreditCard, faMicroscope } from '@fortawesome/free-solid-svg-icons'; + faCircleXmark, faCalendarCheck, faMoneyBillTrendUp, faRobot, faShareNodes, faCreditCard, faMicroscope, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { MenuComponent } from '@components/menu/menu.component'; import { PreviewTitleComponent } from '@components/master-page-preview/preview-title.component'; @@ -94,6 +94,7 @@ import { SatsComponent } from '@app/shared/components/sats/sats.component'; import { BtcComponent } from '@app/shared/components/btc/btc.component'; import { FeeRateComponent } from '@app/shared/components/fee-rate/fee-rate.component'; import { AddressTypeComponent } from '@app/shared/components/address-type/address-type.component'; +import { AddressTextComponent } from '@app/shared/components/address-text/address-text.component'; import { TruncateComponent } from '@app/shared/components/truncate/truncate.component'; import { SearchResultsComponent } from '@components/search-form/search-results/search-results.component'; import { TimestampComponent } from '@app/shared/components/timestamp/timestamp.component'; @@ -214,6 +215,7 @@ import { GithubLogin } from '../components/github-login.component/github-login.c BtcComponent, FeeRateComponent, AddressTypeComponent, + AddressTextComponent, TruncateComponent, SearchResultsComponent, TimestampComponent, @@ -360,6 +362,7 @@ import { GithubLogin } from '../components/github-login.component/github-login.c BtcComponent, FeeRateComponent, AddressTypeComponent, + AddressTextComponent, TruncateComponent, SearchResultsComponent, TimestampComponent, @@ -395,7 +398,6 @@ export class SharedModule { constructor(library: FaIconLibrary) { library.addIcons(faInfoCircle); library.addIcons(faChartArea); - library.addIcons(faTv); library.addIcons(faClock); library.addIcons(faTachometerAlt); library.addIcons(faCubes); @@ -465,5 +467,6 @@ export class SharedModule { library.addIcons(faShareNodes); library.addIcons(faCreditCard); library.addIcons(faMicroscope); + library.addIcons(faExclamationTriangle); } } diff --git a/frontend/src/locale/messages.fr.xlf b/frontend/src/locale/messages.fr.xlf index 51e7204b7..aac7eead9 100644 --- a/frontend/src/locale/messages.fr.xlf +++ b/frontend/src/locale/messages.fr.xlf @@ -1567,7 +1567,7 @@ Total Bid Boost - Augmentation totale des frais + Total frais ajoutés src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html 11 @@ -1728,7 +1728,7 @@ Bid Boost - Augmentation des frais + Frais ajoutés src/app/components/acceleration/accelerations-list/accelerations-list.component.html 17 @@ -6739,7 +6739,7 @@ Just now - Juste maintenant + À l'instant src/app/components/time/time.component.ts 111 @@ -9847,7 +9847,7 @@ confirmation - confirmation + confirmation src/app/shared/components/confirmations/confirmations.component.html 4 diff --git a/production/README.md b/production/README.md index 2805cde81..bf281fb1b 100644 --- a/production/README.md +++ b/production/README.md @@ -84,11 +84,11 @@ pkg install -y zsh sudo git screen curl wget neovim rsync nginx openssl openssh- ### Node.js + npm -Build Node.js v20.17.0 and npm v9 from source using `nvm`: +Build Node.js v22.14.0 and npm v9 from source using `nvm`: ``` curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | zsh source $HOME/.zshrc -nvm install v20.17.0 --shared-zlib +nvm install v22.14.0 --shared-zlib nvm alias default node ``` diff --git a/production/install b/production/install index f3bebb4e7..aadb8005e 100755 --- a/production/install +++ b/production/install @@ -378,7 +378,7 @@ ELEMENTS_REPO_URL=https://github.com/ElementsProject/elements ELEMENTS_REPO_NAME=elements ELEMENTS_REPO_BRANCH=master #ELEMENTS_LATEST_RELEASE=$(curl -s https://api.github.com/repos/ElementsProject/elements/releases/latest|grep tag_name|head -1|cut -d '"' -f4) -ELEMENTS_LATEST_RELEASE=elements-23.2.6 +ELEMENTS_LATEST_RELEASE=elements-23.2.7 echo -n '.' BITCOIN_ELECTRS_REPO_URL=https://github.com/mempool/electrs @@ -1116,8 +1116,8 @@ echo "[*] Installing nvm.sh from GitHub" osSudo "${MEMPOOL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh' echo "[*] Building NodeJS via nvm.sh" -osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm install v20.12.0 --shared-zlib' -osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm alias default 20.12.0' +osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm install v22.14.0 --shared-zlib' +osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm alias default 22.14.0' #################### # Tor installation # @@ -1565,7 +1565,7 @@ EOF osSudo "${UNFURL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh' echo "[*] Building NodeJS via nvm.sh" - osSudo "${UNFURL_USER}" zsh -c 'source ~/.zshrc ; nvm install v20.12.0 --shared-zlib' + osSudo "${UNFURL_USER}" zsh -c 'source ~/.zshrc ; nvm install v22.14.0 --shared-zlib' ;; esac diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json index 9505601d2..87f58f916 100644 --- a/production/mempool-config.mainnet.json +++ b/production/mempool-config.mainnet.json @@ -159,6 +159,7 @@ }, "WALLETS": { "ENABLED": true, + "AUTO": true, "WALLETS": ["BITB", "3350"] }, "STRATUM": { diff --git a/production/mempool-start-all b/production/mempool-start-all index 9d4c6ee58..f6c3c76c2 100755 --- a/production/mempool-start-all +++ b/production/mempool-start-all @@ -1,7 +1,7 @@ #!/usr/bin/env zsh export NVM_DIR="$HOME/.nvm" source "$NVM_DIR/nvm.sh" -nvm use v20.12.0 +nvm use v22.14.0 # start all mempool backends that exist for site in mainnet mainnet-lightning testnet testnet-lightning testnet4 signet signet-lightning liquid liquidtestnet;do