From 0e6c23fdecbbc5ac07caffe8e45ae8d45abdc10f Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Sun, 29 Jun 2025 06:12:54 +0900 Subject: [PATCH 01/57] create chain page and added header with link to blocks --- packages/insight/src/Routing.tsx | 2 + .../insight/src/components/chain-header.tsx | 92 +++++++++++++++++++ .../insight/src/components/currency-tile.tsx | 6 +- packages/insight/src/pages/chain.tsx | 54 +++++++++++ packages/insight/src/pages/index.tsx | 3 - 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 packages/insight/src/components/chain-header.tsx create mode 100644 packages/insight/src/pages/chain.tsx diff --git a/packages/insight/src/Routing.tsx b/packages/insight/src/Routing.tsx index 796f2eec62d..00859c9a290 100644 --- a/packages/insight/src/Routing.tsx +++ b/packages/insight/src/Routing.tsx @@ -1,6 +1,7 @@ import React, {lazy, Suspense} from 'react'; import {Navigate, Route, Routes} from 'react-router-dom'; import Home from './pages'; +import {ChainDetails} from './pages/chain'; const Blocks = lazy(() => import('./pages/blocks')); const Block = lazy(() => import('./pages/block')); const TransactionHash = lazy(() => import('./pages/transaction')); @@ -12,6 +13,7 @@ function Routing() { } /> + } /> } /> } /> } /> diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx new file mode 100644 index 00000000000..85f62e39cf1 --- /dev/null +++ b/packages/insight/src/components/chain-header.tsx @@ -0,0 +1,92 @@ +import {FC, useEffect, useRef} from 'react'; +import {useApi} from 'src/api/api'; +import {buildTime, getApiRoot, getDefaultRefreshInterval} from 'src/utilities/helper-methods'; +import { Chart as ChartJS } from 'chart.js'; +import { colorCodes } from 'src/utilities/constants'; + + +const ChainHeader: FC<{currency: string, network: string}> = ({currency, network}) => { + const apiRoot = getApiRoot(currency); + const refreshInterval = getDefaultRefreshInterval(currency); + const {data, error} = useApi(`${apiRoot}/${currency}/mainnet/block?limit=1`, {refreshInterval}); + const {height, time, transactionCount, size} = data[0]; + + const {data: priceDetails} = useApi(`https://bitpay.com/rates/${currency}/usd`); + const {data: priceDisplay} = useApi( + `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, + ); + + const chartRef = useRef(null); + const chartInstanceRef = useRef(null); + + const price = priceDetails?.data?.rate; + const priceList = priceDisplay?.data?.[0]?.priceDisplay || []; + + const chartData = { + labels: priceList, + datasets: [ + { + data: priceList, + fill: false, + spanGaps: true, + borderColor: colorCodes[currency], + borderWidth: 2, + pointRadius: 0, + }, + ], + }; + + const options = { + scales: { + x: {display: false}, + y: {display: false}, + }, + plugins: {legend: {display: false}}, + events: [], // disable default events + responsive: true, + maintainAspectRatio: false, + tension: 0.5, + }; + + useEffect(() => { + if (chartRef.current) { + if (chartInstanceRef.current) { + chartInstanceRef.current.destroy(); + } + + chartInstanceRef.current = new ChartJS(chartRef.current, { + type: 'line', + data: chartData, + options, + }); + } + + return () => { + chartInstanceRef.current?.destroy(); + }; + }, [chartData, options]); + + + return ( +
+
+ {currency} + {priceList.length > 0 && ( +
+ +
+ )} +
+
+ {price} USD + Height {height} + Mined {buildTime(time)} + Transaction {transactionCount} + Size {size} + +
+
+ ); +} + +export default ChainHeader; \ No newline at end of file diff --git a/packages/insight/src/components/currency-tile.tsx b/packages/insight/src/components/currency-tile.tsx index 5c831779d6d..782aeed5de1 100644 --- a/packages/insight/src/components/currency-tile.tsx +++ b/packages/insight/src/components/currency-tile.tsx @@ -197,12 +197,12 @@ const CurrencyTile: FC = ({currency}) => { const {height, time, transactionCount, size} = data[0]; const imgSrc = `https://bitpay.com/img/icon/currencies/${currency}.svg`; - const gotoAllBlocks = async () => { - await navigate(`/${currency}/mainnet/blocks`); + const gotoChain = async () => { + await navigate(`/${currency}/mainnet/chain`); }; return ( - + {`${currency}
diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx new file mode 100644 index 00000000000..0e33c4a235b --- /dev/null +++ b/packages/insight/src/pages/chain.tsx @@ -0,0 +1,54 @@ +import { useEffect } from "react"; +import { useDispatch } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { useApi } from "src/api/api"; +import ChainHeader from "src/components/chain-header"; +import { changeCurrency, changeNetwork } from "src/store/app.actions"; +import { getApiRoot, getDefaultRefreshInterval, normalizeParams } from "src/utilities/helper-methods"; + + +export const ChainDetails = () => { + const params = useParams<{currency: string, network: string}>() + let {currency, network} = params; + + const navigate = useNavigate(); + const dispatch = useDispatch(); + + if (currency) { + const apiRoot = getApiRoot(currency); + const refreshInterval = getDefaultRefreshInterval(currency); + const {data, error} = useApi(`${apiRoot}/${currency}/mainnet/block?limit=1`, {refreshInterval}); + if (data) { + const {height, time, transactionCount, size} = data[0]; + + } + + } + + useEffect(() => { + if (!currency || !network) return; + + const normalizedParams = normalizeParams(currency, network); + currency = normalizedParams.currency; + network = normalizedParams.network; + dispatch(changeCurrency(currency)); + dispatch(changeNetwork(network)); + + }, [currency, network]); + + const {data: priceDetails} = useApi(`https://bitpay.com/rates/${currency}/usd`); + const price = priceDetails?.data?.rate; + + const gotoBlocks = async () => { + await navigate(`/${currency}/mainnet/blocks`); + } + + return ( + <> + {currency && network && } +
+
Go to full block list
+
+ + ); +} diff --git a/packages/insight/src/pages/index.tsx b/packages/insight/src/pages/index.tsx index f24f49be01b..072433425be 100644 --- a/packages/insight/src/pages/index.tsx +++ b/packages/insight/src/pages/index.tsx @@ -1,5 +1,4 @@ import {SUPPORTED_CURRENCIES} from '../utilities/constants'; -import {SecondaryTitle} from '../assets/styles/titles'; import CurrencyTile from '../components/currency-tile'; import Masonry from 'react-masonry-css'; import {motion} from 'framer-motion'; @@ -24,8 +23,6 @@ const Home: React.FC = () => { return ( - Latest Blocks - Date: Tue, 8 Jul 2025 23:21:38 +0900 Subject: [PATCH 02/57] added block list to chain page --- packages/insight/src/pages/chain.tsx | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 0e33c4a235b..74524bf0075 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,10 +1,11 @@ -import { useEffect } from "react"; -import { useDispatch } from "react-redux"; -import { useNavigate, useParams } from "react-router-dom"; -import { useApi } from "src/api/api"; -import ChainHeader from "src/components/chain-header"; -import { changeCurrency, changeNetwork } from "src/store/app.actions"; -import { getApiRoot, getDefaultRefreshInterval, normalizeParams } from "src/utilities/helper-methods"; +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useApi } from 'src/api/api'; +import ChainHeader from 'src/components/chain-header'; +import { changeCurrency, changeNetwork } from 'src/store/app.actions'; +import { getApiRoot, getDefaultRefreshInterval, normalizeParams } from 'src/utilities/helper-methods'; +import Blocks from './blocks'; export const ChainDetails = () => { @@ -46,8 +47,16 @@ export const ChainDetails = () => { return ( <> {currency && network && } -
-
Go to full block list
+
+
+ Fee +
+
+
+
Go to full block list
+
+ +
); From 56ba11e313257ee96dc86b36f15fe209976a67ab Mon Sep 17 00:00:00 2001 From: Gabriel Date: Mon, 7 Jul 2025 15:57:38 -0300 Subject: [PATCH 03/57] [REF] replace oneinch with coingecko for token lists --- packages/bitcore-wallet-service/src/config.ts | 3 +- .../src/lib/common/defaults.ts | 2 +- .../src/lib/expressapp.ts | 3 +- .../bitcore-wallet-service/src/lib/server.ts | 74 ++++++++++++++++++- .../src/transactions/sol/index.ts | 2 +- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/packages/bitcore-wallet-service/src/config.ts b/packages/bitcore-wallet-service/src/config.ts index 6b29352f3f3..cd8b22940cf 100644 --- a/packages/bitcore-wallet-service/src/config.ts +++ b/packages/bitcore-wallet-service/src/config.ts @@ -466,7 +466,8 @@ const Config = (): any => { // referrerFee: 'one_inch_referrer_fee', // min: 0; max: 3; (represents percentage) // }, // coinGecko: { - // api: 'https://api.coingecko.com/api', + // api: 'https://pro-api.coingecko.com/api/', + // apiKey: 'coin_gecko_api_key', // }, // moralis: { // apiKey: 'moralis_api_key_here', diff --git a/packages/bitcore-wallet-service/src/lib/common/defaults.ts b/packages/bitcore-wallet-service/src/lib/common/defaults.ts index 78c917fd155..3c8fbcbd77a 100644 --- a/packages/bitcore-wallet-service/src/lib/common/defaults.ts +++ b/packages/bitcore-wallet-service/src/lib/common/defaults.ts @@ -294,7 +294,7 @@ export const Defaults = { ONE_INCH_CACHE_DURATION: 1 * 60 * 1000, // Coingecko token rates cache duration (in ms) - COIN_GECKO_CACHE_DURATION: 5 * 60 * 1000, + COIN_GECKO_CACHE_DURATION: 1 * 24 * 60 * 60 * 1000, // Max allowed timespan for notification queries in seconds MAX_NOTIFICATIONS_TIMESPAN: 60 * 60 * 24 * 14, // ~ 2 weeks diff --git a/packages/bitcore-wallet-service/src/lib/expressapp.ts b/packages/bitcore-wallet-service/src/lib/expressapp.ts index a85a96aaf81..3b4aae47872 100644 --- a/packages/bitcore-wallet-service/src/lib/expressapp.ts +++ b/packages/bitcore-wallet-service/src/lib/expressapp.ts @@ -2228,8 +2228,7 @@ export class ExpressApp { } catch (ex) { return returnError(ex, res, req); } - server.externalServices.oneInch - .oneInchGetTokens(req) + server.coinGeckoGetTokens(req) .then(response => { res.json(response); }) diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index 8edb295ce7b..042b14d2ce5 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -5273,6 +5273,7 @@ export class WalletService implements IWalletService { const credentials = { API: config.coinGecko.api, + API_KEY: config.coinGecko.apiKey, }; return credentials; @@ -5312,8 +5313,79 @@ export class WalletService implements IWalletService { ); }); } -} + coinGeckoGetTokens(req): Promise { + return new Promise((resolve, reject) => { + const chain = req.params?.['chain'] || 'ethereum'; + const cacheKey = `cgTokenList:${chain}`; + const credentials = this.coinGeckoGetCredentials(); + + this.storage.checkAndUseGlobalCache(cacheKey, Defaults.COIN_GECKO_CACHE_DURATION, (err, values, oldvalues) => { + if (err) logger.warn('Cache check failed', err); + if (values) return resolve(values); + + const assetPlatformMap = { + eth: 'ethereum', + matic: 'polygon-pos', + pol: 'polygon-pos', + arb: 'arbitrum-one', + base: 'base', + op: 'optimistic-ethereum', + sol: 'solana', + }; + + const assetId = assetPlatformMap[chain]; + if (!assetId) return reject(new Error(`Unsupported chain '${chain}'`)); + + const URL: string = `${credentials.API}/v3/token_lists/${assetId}/all.json`; + const headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'x-cg-pro-api-key': credentials.API_KEY + }; + + this.request.get( + URL, + { + headers, + json: true + }, + (err, data) => { + const tokens = data?.body?.tokens; + const status = data?.body?.status; + if (err) { + logger.warn('An error occured while retrieving the token list', err); + if (oldvalues) { + logger.warn('Using old cached values'); + return resolve(oldvalues); + } + return reject(err.body ?? err); + } else if (status?.error_code === 429 && oldvalues) { + return resolve(oldvalues); + } else { + if (!tokens) { + if (oldvalues) { + logger.warn('No token list available... using old cached values'); + return resolve(oldvalues); + } + return reject(new Error(`Could not get tokens list. Code: ${status?.error_code}. Error: ${status?.error_message || 'Unknown error'}`)); + } + const updatedTokens = tokens.map(token => { + if (token.logoURI?.includes('/thumb/')) { + token.logoURI = token.logoURI.replace('/thumb/', '/large/'); + } + return token; + }); + this.storage.storeGlobalCache(cacheKey, updatedTokens, storeErr => { + if (storeErr) logger.warn('Could not cache token list', storeErr); + return resolve(updatedTokens); + }); + } + }); + }); + }); + } +} export function checkRequired(obj, args, cb?: (e: any) => void) { const missing = Utils.getMissingFields(obj, args); diff --git a/packages/crypto-wallet-core/src/transactions/sol/index.ts b/packages/crypto-wallet-core/src/transactions/sol/index.ts index 522799fc9d3..6d3f5d26ce4 100644 --- a/packages/crypto-wallet-core/src/transactions/sol/index.ts +++ b/packages/crypto-wallet-core/src/transactions/sol/index.ts @@ -1,5 +1,5 @@ import * as SolComputeBudget from '@solana-program/compute-budget'; -import * as SolComputeMemo from "@solana-program/memo"; +import * as SolComputeMemo from '@solana-program/memo'; import * as SolSystem from '@solana-program/system'; import * as SolKit from '@solana/kit' import { Key } from '../../derivation'; From 180c2a1ac964a65f608e4c819dcde2a38a0d9af3 Mon Sep 17 00:00:00 2001 From: Justin Langston Date: Tue, 8 Jul 2025 14:23:11 -0400 Subject: [PATCH 04/57] v10.10.6 --- lerna.json | 2 +- packages/bitcore-client/package-lock.json | 4 ++-- packages/bitcore-client/package.json | 4 ++-- packages/bitcore-node/package-lock.json | 4 ++-- packages/bitcore-node/package.json | 8 ++++---- packages/bitcore-tss/package-lock.json | 4 ++-- packages/bitcore-tss/package.json | 4 ++-- packages/bitcore-wallet-client/package-lock.json | 8 ++++---- packages/bitcore-wallet-client/package.json | 6 +++--- packages/bitcore-wallet-service/package-lock.json | 6 +++--- packages/bitcore-wallet-service/package.json | 4 ++-- packages/bitcore-wallet/package-lock.json | 4 ++-- packages/bitcore-wallet/package.json | 4 ++-- packages/crypto-wallet-core/package-lock.json | 4 ++-- packages/crypto-wallet-core/package.json | 2 +- 15 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lerna.json b/lerna.json index 7fae21c8d26..eb7b43b2dfe 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "lerna": "2.9.1", - "version": "10.10.5", + "version": "10.10.6", "packages": ["packages/[^insight]*"] } diff --git a/packages/bitcore-client/package-lock.json b/packages/bitcore-client/package-lock.json index 884bad20dda..bb6836a2450 100644 --- a/packages/bitcore-client/package-lock.json +++ b/packages/bitcore-client/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-client", - "version": "10.10.5", + "version": "10.10.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-client", - "version": "10.10.5", + "version": "10.10.6", "dependencies": { "async": "2.5.0", "bcrypt": "5.1.0", diff --git a/packages/bitcore-client/package.json b/packages/bitcore-client/package.json index 3253704f3fc..e454d48f254 100644 --- a/packages/bitcore-client/package.json +++ b/packages/bitcore-client/package.json @@ -1,7 +1,7 @@ { "name": "bitcore-client", "description": "Wallet client for Bitcore node", - "version": "10.10.5", + "version": "10.10.6", "author": "Justin Langston ", "main": "./ts_build/src/index.js", "types": "./ts_build/src/index.d.ts", @@ -28,7 +28,7 @@ "bcrypt": "5.1.0", "bitcore-mnemonic": "^10.10.5", "commander": "11.1.0", - "crypto-wallet-core": "^10.10.5", + "crypto-wallet-core": "^10.10.6", "level-js": "4.0.2", "leveldown": "6.1.1", "levelup": "4.3.2", diff --git a/packages/bitcore-node/package-lock.json b/packages/bitcore-node/package-lock.json index 7cf14abe1ee..ca3497a87ad 100644 --- a/packages/bitcore-node/package-lock.json +++ b/packages/bitcore-node/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-node", - "version": "10.10.5", + "version": "10.10.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-node", - "version": "10.10.5", + "version": "10.10.6", "license": "MIT", "dependencies": { "abi-decoder": "2.4.0", diff --git a/packages/bitcore-node/package.json b/packages/bitcore-node/package.json index 5a9051bf584..1e22fb625f3 100644 --- a/packages/bitcore-node/package.json +++ b/packages/bitcore-node/package.json @@ -4,7 +4,7 @@ "engines": { "node": ">=18.0.0" }, - "version": "10.10.5", + "version": "10.10.6", "author": "Justin Langston ", "contributors": [ "Justin Langston ", @@ -90,7 +90,7 @@ }, "dependencies": { "abi-decoder": "2.4.0", - "bitcore-client": "^10.10.5", + "bitcore-client": "^10.10.6", "bitcore-lib": "^10.10.5", "bitcore-lib-cash": "^10.10.5", "bitcore-lib-doge": "^10.10.5", @@ -98,10 +98,10 @@ "bitcore-p2p": "^10.10.5", "bitcore-p2p-cash": "^10.10.5", "bitcore-p2p-doge": "^10.10.5", - "bitcore-wallet-client": "^10.10.5", + "bitcore-wallet-client": "^10.10.6", "cors": "2.8.4", "crypto-rpc": "https://github.com/bitpay/crypto-rpc.git#370b201c98f4616b378290dc30486df5c1c769e0", - "crypto-wallet-core": "^10.10.5", + "crypto-wallet-core": "^10.10.6", "express": "4.19.2", "lodash": "4.17.11", "mongodb": "3.6.12", diff --git a/packages/bitcore-tss/package-lock.json b/packages/bitcore-tss/package-lock.json index b210fcfd10a..5e0c3b869e2 100644 --- a/packages/bitcore-tss/package-lock.json +++ b/packages/bitcore-tss/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-tss", - "version": "10.10.5", + "version": "10.10.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-tss", - "version": "10.10.5", + "version": "10.10.6", "license": "MIT", "dependencies": { "@bitgo/sdk-lib-mpc": "^10.1.2" diff --git a/packages/bitcore-tss/package.json b/packages/bitcore-tss/package.json index 37a07858730..43127536183 100644 --- a/packages/bitcore-tss/package.json +++ b/packages/bitcore-tss/package.json @@ -1,6 +1,6 @@ { "name": "bitcore-tss", - "version": "10.10.5", + "version": "10.10.6", "description": "Bitcore Library for Threshold Signature Schemes", "main": "index.js", "type": "commonjs", @@ -31,7 +31,7 @@ "@wdio/spec-reporter": "^9.11.0", "assert": "^2.1.0", "browserify": "^17.0.1", - "crypto-wallet-core": "^10.10.5", + "crypto-wallet-core": "^10.10.6", "mocha": "11.1.0", "webdriverio": "^9.12.0", "webpack": "^5.98.0", diff --git a/packages/bitcore-wallet-client/package-lock.json b/packages/bitcore-wallet-client/package-lock.json index f7497fe55fa..284a7823ebf 100644 --- a/packages/bitcore-wallet-client/package-lock.json +++ b/packages/bitcore-wallet-client/package-lock.json @@ -1,19 +1,19 @@ { "name": "bitcore-wallet-client", - "version": "10.10.5", + "version": "10.10.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet-client", - "version": "10.10.5", + "version": "10.10.6", "license": "MIT", "dependencies": { "ajv": "6.12.0", "async": "0.9.2", "bip38": "1.4.0", "bitcore-mnemonic": "^10.10.5", - "crypto-wallet-core": "^10.10.5", + "crypto-wallet-core": "^10.10.6", "json-stable-stringify": "1.0.1", "preconditions": "2.2.3", "sjcl": "1.0.3", @@ -25,7 +25,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.5", + "bitcore-wallet-service": "^10.10.6", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-client/package.json b/packages/bitcore-wallet-client/package.json index 828295d0af8..2210dcafaff 100644 --- a/packages/bitcore-wallet-client/package.json +++ b/packages/bitcore-wallet-client/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-client", "description": "Client for bitcore-wallet-service", "author": "BitPay Inc", - "version": "10.10.5", + "version": "10.10.6", "license": "MIT", "main": "ts_build/index.js", "types": "ts_build/index.d.js", @@ -31,7 +31,7 @@ "async": "0.9.2", "bip38": "1.4.0", "bitcore-mnemonic": "^10.10.5", - "crypto-wallet-core": "^10.10.5", + "crypto-wallet-core": "^10.10.6", "json-stable-stringify": "1.0.1", "preconditions": "2.2.3", "sjcl": "1.0.3", @@ -43,7 +43,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.5", + "bitcore-wallet-service": "^10.10.6", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-service/package-lock.json b/packages/bitcore-wallet-service/package-lock.json index 60a372971d8..1bf4e117329 100644 --- a/packages/bitcore-wallet-service/package-lock.json +++ b/packages/bitcore-wallet-service/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet-service", - "version": "10.10.5", + "version": "10.10.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bitcore-wallet-service", - "version": "10.10.5", + "version": "10.10.6", "license": "MIT", "dependencies": { "@sendgrid/mail": "6.5.4", @@ -18,7 +18,7 @@ "bitcore-lib-ltc": "^10.10.5", "compression": "1.7.4", "cors": "2.8.5", - "crypto-wallet-core": "^10.10.5", + "crypto-wallet-core": "^10.10.6", "email-validator": "1.2.3", "express": "4.19.2", "express-rate-limit": "2.14.2", diff --git a/packages/bitcore-wallet-service/package.json b/packages/bitcore-wallet-service/package.json index 8c73da22d65..9a764033e56 100644 --- a/packages/bitcore-wallet-service/package.json +++ b/packages/bitcore-wallet-service/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-service", "description": "A service for Mutisig HD Bitcoin Wallets", "author": "BitPay Inc", - "version": "10.10.5", + "version": "10.10.6", "license": "MIT", "main": "ts_build/index.js", "types": "./ts_build/index.d.ts", @@ -50,7 +50,7 @@ "bitcore-lib-ltc": "^10.10.5", "compression": "1.7.4", "cors": "2.8.5", - "crypto-wallet-core": "^10.10.5", + "crypto-wallet-core": "^10.10.6", "email-validator": "1.2.3", "express": "4.19.2", "express-rate-limit": "2.14.2", diff --git a/packages/bitcore-wallet/package-lock.json b/packages/bitcore-wallet/package-lock.json index 4ae13fd566d..2c5532d965b 100644 --- a/packages/bitcore-wallet/package-lock.json +++ b/packages/bitcore-wallet/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet", - "version": "10.10.5", + "version": "10.10.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet", - "version": "10.10.5", + "version": "10.10.6", "dependencies": { "async": "^2.5.0", "commander": "^2.6.0", diff --git a/packages/bitcore-wallet/package.json b/packages/bitcore-wallet/package.json index d5967261741..5c3aa92fd69 100644 --- a/packages/bitcore-wallet/package.json +++ b/packages/bitcore-wallet/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet", "description": "A CLI Mutisig HD Bitcoin Wallet, demo for Bitcore Wallet Service", "author": "BitPay Inc", - "version": "10.10.5", + "version": "10.10.6", "private": true, "keywords": [ "bitcoin", @@ -20,7 +20,7 @@ "dependencies": { "async": "^2.5.0", "bitcore-lib": "^10.10.5", - "bitcore-wallet-client": "^10.10.5", + "bitcore-wallet-client": "^10.10.6", "commander": "^2.6.0", "lodash": "^3.3.1", "moment": "^2.9.0", diff --git a/packages/crypto-wallet-core/package-lock.json b/packages/crypto-wallet-core/package-lock.json index 53534df1f12..806e17cdfd2 100644 --- a/packages/crypto-wallet-core/package-lock.json +++ b/packages/crypto-wallet-core/package-lock.json @@ -1,12 +1,12 @@ { "name": "crypto-wallet-core", - "version": "10.10.5", + "version": "10.10.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "crypto-wallet-core", - "version": "10.10.5", + "version": "10.10.6", "license": "MIT", "dependencies": { "@solana-program/compute-budget": "^0.7.0", diff --git a/packages/crypto-wallet-core/package.json b/packages/crypto-wallet-core/package.json index 7739c9d772d..dc9540a31bf 100644 --- a/packages/crypto-wallet-core/package.json +++ b/packages/crypto-wallet-core/package.json @@ -1,6 +1,6 @@ { "name": "crypto-wallet-core", - "version": "10.10.5", + "version": "10.10.6", "description": "A multi-currency support library for address derivation, private key creation, and transaction creation", "main": "./ts_build/src/index.js", "types": "./ts_build/src/index.d.ts", From 6bcac67b0debcb1c3b091a1149704191ea668bdc Mon Sep 17 00:00:00 2001 From: Sayrix <43046854+Sayrix@users.noreply.github.com> Date: Tue, 1 Jul 2025 17:59:37 +0200 Subject: [PATCH 05/57] prevent redefine _getX/Y From https://github.com/bitpay/bitcore/pull/3205 --- packages/bitcore-lib/lib/crypto/point.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/bitcore-lib/lib/crypto/point.js b/packages/bitcore-lib/lib/crypto/point.js index 762431a8afa..bf29c12a26b 100644 --- a/packages/bitcore-lib/lib/crypto/point.js +++ b/packages/bitcore-lib/lib/crypto/point.js @@ -81,6 +81,7 @@ Point.getP = function() { return ec.curve.p.clone(); }; +if (!Point.prototype._getX) Point.prototype._getX = Point.prototype.getX; /** @@ -93,6 +94,7 @@ Point.prototype.getX = function getX() { return new BN(this._getX().toArray()); }; +if (!Point.prototype._getY) Point.prototype._getY = Point.prototype.getY; /** From ce76c31e61c90ab9fa9ea9cbbebd5d8a63e1fef9 Mon Sep 17 00:00:00 2001 From: lyambo Date: Thu, 3 Jul 2025 10:31:02 -0400 Subject: [PATCH 06/57] verify prePublishRaw tx --- packages/bitcore-wallet-client/src/lib/verifier.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/bitcore-wallet-client/src/lib/verifier.ts b/packages/bitcore-wallet-client/src/lib/verifier.ts index 51d3a644b90..0890b598e30 100644 --- a/packages/bitcore-wallet-client/src/lib/verifier.ts +++ b/packages/bitcore-wallet-client/src/lib/verifier.ts @@ -217,8 +217,13 @@ export class Verifier { ' Signature: ', txp.proposalSignature ); - if (!Utils.verifyMessage(hash, txp.proposalSignature, creatorSigningPubKey)) - return false; + + const verified = Utils.verifyMessage(hash, txp.proposalSignature, creatorSigningPubKey); + if (!verified && !txp.prePublishRaw) + return false; + + if (!verified && txp.prePublishRaw && !Utils.verifyMessage(txp.prePublishRaw, txp.proposalSignature, creatorSigningPubKey)) + return false; if (Constants.UTXO_CHAINS.includes(chain)) { if (!this.checkAddress(credentials, txp.changeAddress)) { From 6ccb7d9d17d6d7afa947ea5c0a1bd68edceb328d Mon Sep 17 00:00:00 2001 From: lyambo Date: Thu, 3 Jul 2025 10:33:59 -0400 Subject: [PATCH 07/57] Adding sourceAddress for SPL proposals --- packages/bitcore-wallet-service/src/lib/model/txproposal.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/bitcore-wallet-service/src/lib/model/txproposal.ts b/packages/bitcore-wallet-service/src/lib/model/txproposal.ts index 046d18d38d8..e487112a3ce 100644 --- a/packages/bitcore-wallet-service/src/lib/model/txproposal.ts +++ b/packages/bitcore-wallet-service/src/lib/model/txproposal.ts @@ -37,6 +37,7 @@ export interface ITxProposal { amount: number; address: string; toAddress?: string; + sourceAddress?: string; message?: string; data?: string; gasLimit?: number; @@ -118,6 +119,7 @@ export class TxProposal { amount: number; address?: string; toAddress?: string; + sourceAddress?: string; message?: string; data?: string; gasLimit?: number; @@ -214,6 +216,7 @@ export class TxProposal { const out: any = {}; if (output.amount !== undefined) out.amount = output.amount; if (output.toAddress !== undefined) out.toAddress = output.toAddress; + if (output.sourceAddress !== undefined) out.sourceAddress = output.sourceAddress; if (output.message !== undefined) out.message = output.message; if (output.data !== undefined) out.data = output.data; if (output.gasLimit !== undefined) out.gasLimit = output.gasLimit; From 46ebbd762dc09374fc6bd9e36f5b52c0215861e6 Mon Sep 17 00:00:00 2001 From: lyambo Date: Thu, 3 Jul 2025 11:32:14 -0400 Subject: [PATCH 08/57] add all recipients by default --- .../bitcore-wallet-client/src/lib/common/utils.ts | 10 +++++++++- .../bitcore-wallet-service/src/lib/chain/sol/index.ts | 11 ++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/bitcore-wallet-client/src/lib/common/utils.ts b/packages/bitcore-wallet-client/src/lib/common/utils.ts index ddbb4862330..e20fcd06bb8 100644 --- a/packages/bitcore-wallet-client/src/lib/common/utils.ts +++ b/packages/bitcore-wallet-client/src/lib/common/utils.ts @@ -441,7 +441,7 @@ export class Utils { return t; } else { - // ETH ERC20 XRP + // ETH ERC20 XRP SOL const { data, destinationTag, @@ -511,6 +511,14 @@ export class Utils { }); unsignedTxs.push(rawTx); } + } else if (chainName === 'SOL') { + const rawTx = Transactions.create({ + ...txp, + tag: destinationTag ? Number(destinationTag) : undefined, + chain: _chain, + recipients, + }); + unsignedTxs.push(rawTx); } else { for (let index = 0; index < recipients.length; index++) { const rawTx = Transactions.create({ diff --git a/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts b/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts index c20bd1a5b09..30f5605d0df 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts @@ -118,20 +118,13 @@ export class SolChain implements IChain { if (data) { recipients[0].data = data; } - const unsignedTxs = []; - for (let index = 0; index < recipients.length; index++) { - let params = { - ...recipients[index], - recipients: [recipients[index]] - }; - unsignedTxs.push(Transactions.create({ ...txp, chain, ...params })); - } + const unsignedTxs = [Transactions.create({ ...txp, chain, recipients })]; const tx = { uncheckedSerialize: () => unsignedTxs, txid: () => txp.txid, toObject: () => { - let ret = _.clone(txp) + const ret = _.clone(txp); ret.outputs[0].satoshis = ret.outputs[0].amount; return ret; }, From b324db6fcfd73583c52d526e505466d0b9880a1c Mon Sep 17 00:00:00 2001 From: lyambo Date: Thu, 3 Jul 2025 14:18:11 -0400 Subject: [PATCH 09/57] feedback --- packages/bitcore-wallet-client/src/lib/common/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bitcore-wallet-client/src/lib/common/utils.ts b/packages/bitcore-wallet-client/src/lib/common/utils.ts index e20fcd06bb8..c6be7e89c20 100644 --- a/packages/bitcore-wallet-client/src/lib/common/utils.ts +++ b/packages/bitcore-wallet-client/src/lib/common/utils.ts @@ -514,7 +514,6 @@ export class Utils { } else if (chainName === 'SOL') { const rawTx = Transactions.create({ ...txp, - tag: destinationTag ? Number(destinationTag) : undefined, chain: _chain, recipients, }); From 8c2b6bb8738f6f5f13694428b10b4c666ddc8666 Mon Sep 17 00:00:00 2001 From: Justin Langston Date: Mon, 21 Jul 2025 11:01:29 -0400 Subject: [PATCH 10/57] v10.10.7 --- lerna.json | 2 +- packages/bitcore-client/package-lock.json | 4 ++-- packages/bitcore-client/package.json | 6 +++--- packages/bitcore-lib/package-lock.json | 4 ++-- packages/bitcore-lib/package.json | 2 +- packages/bitcore-mnemonic/package-lock.json | 4 ++-- packages/bitcore-mnemonic/package.json | 4 ++-- packages/bitcore-node/package-lock.json | 4 ++-- packages/bitcore-node/package.json | 14 +++++++------- packages/bitcore-p2p-cash/package-lock.json | 4 ++-- packages/bitcore-p2p-cash/package.json | 4 ++-- packages/bitcore-p2p/package-lock.json | 4 ++-- packages/bitcore-p2p/package.json | 4 ++-- packages/bitcore-tss/package-lock.json | 4 ++-- packages/bitcore-tss/package.json | 6 +++--- packages/bitcore-wallet-client/package-lock.json | 10 +++++----- packages/bitcore-wallet-client/package.json | 8 ++++---- packages/bitcore-wallet-service/package-lock.json | 8 ++++---- packages/bitcore-wallet-service/package.json | 6 +++--- packages/bitcore-wallet/package-lock.json | 4 ++-- packages/bitcore-wallet/package.json | 6 +++--- packages/crypto-wallet-core/package-lock.json | 6 +++--- packages/crypto-wallet-core/package.json | 4 ++-- 23 files changed, 61 insertions(+), 61 deletions(-) diff --git a/lerna.json b/lerna.json index eb7b43b2dfe..be8d63354b3 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "lerna": "2.9.1", - "version": "10.10.6", + "version": "10.10.7", "packages": ["packages/[^insight]*"] } diff --git a/packages/bitcore-client/package-lock.json b/packages/bitcore-client/package-lock.json index bb6836a2450..5e1c8c79442 100644 --- a/packages/bitcore-client/package-lock.json +++ b/packages/bitcore-client/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-client", - "version": "10.10.6", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-client", - "version": "10.10.6", + "version": "10.10.7", "dependencies": { "async": "2.5.0", "bcrypt": "5.1.0", diff --git a/packages/bitcore-client/package.json b/packages/bitcore-client/package.json index e454d48f254..08eeb040d51 100644 --- a/packages/bitcore-client/package.json +++ b/packages/bitcore-client/package.json @@ -1,7 +1,7 @@ { "name": "bitcore-client", "description": "Wallet client for Bitcore node", - "version": "10.10.6", + "version": "10.10.7", "author": "Justin Langston ", "main": "./ts_build/src/index.js", "types": "./ts_build/src/index.d.ts", @@ -26,9 +26,9 @@ "JSONStream": "~1.3.1", "async": "2.5.0", "bcrypt": "5.1.0", - "bitcore-mnemonic": "^10.10.5", + "bitcore-mnemonic": "^10.10.7", "commander": "11.1.0", - "crypto-wallet-core": "^10.10.6", + "crypto-wallet-core": "^10.10.7", "level-js": "4.0.2", "leveldown": "6.1.1", "levelup": "4.3.2", diff --git a/packages/bitcore-lib/package-lock.json b/packages/bitcore-lib/package-lock.json index 16a5a126ac2..963d42bac9d 100644 --- a/packages/bitcore-lib/package-lock.json +++ b/packages/bitcore-lib/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-lib", - "version": "10.10.5", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-lib", - "version": "10.10.5", + "version": "10.10.7", "license": "MIT", "dependencies": { "bech32": "=2.0.0", diff --git a/packages/bitcore-lib/package.json b/packages/bitcore-lib/package.json index 5bada641807..e7164779c85 100644 --- a/packages/bitcore-lib/package.json +++ b/packages/bitcore-lib/package.json @@ -1,6 +1,6 @@ { "name": "bitcore-lib", - "version": "10.10.5", + "version": "10.10.7", "description": "A pure and powerful JavaScript Bitcoin library.", "author": "BitPay ", "main": "index.js", diff --git a/packages/bitcore-mnemonic/package-lock.json b/packages/bitcore-mnemonic/package-lock.json index a895916c633..b14f6651e84 100644 --- a/packages/bitcore-mnemonic/package-lock.json +++ b/packages/bitcore-mnemonic/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-mnemonic", - "version": "10.10.5", + "version": "10.10.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bitcore-mnemonic", - "version": "10.10.5", + "version": "10.10.7", "license": "MIT", "dependencies": { "unorm": "^1.4.1" diff --git a/packages/bitcore-mnemonic/package.json b/packages/bitcore-mnemonic/package.json index 4a22d7ad0f9..82214a4eb8e 100644 --- a/packages/bitcore-mnemonic/package.json +++ b/packages/bitcore-mnemonic/package.json @@ -1,6 +1,6 @@ { "name": "bitcore-mnemonic", - "version": "10.10.5", + "version": "10.10.7", "description": "BIP39 Mnemonics implemented for Bitcore.", "author": "BitPay ", "main": "index.js", @@ -43,7 +43,7 @@ "mocha": "^5.2.0" }, "dependencies": { - "bitcore-lib": "^10.10.5", + "bitcore-lib": "^10.10.7", "unorm": "^1.4.1" }, "peerDependencies": { diff --git a/packages/bitcore-node/package-lock.json b/packages/bitcore-node/package-lock.json index ca3497a87ad..e2506f993f1 100644 --- a/packages/bitcore-node/package-lock.json +++ b/packages/bitcore-node/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-node", - "version": "10.10.6", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-node", - "version": "10.10.6", + "version": "10.10.7", "license": "MIT", "dependencies": { "abi-decoder": "2.4.0", diff --git a/packages/bitcore-node/package.json b/packages/bitcore-node/package.json index 1e22fb625f3..cb0151ac883 100644 --- a/packages/bitcore-node/package.json +++ b/packages/bitcore-node/package.json @@ -4,7 +4,7 @@ "engines": { "node": ">=18.0.0" }, - "version": "10.10.6", + "version": "10.10.7", "author": "Justin Langston ", "contributors": [ "Justin Langston ", @@ -90,18 +90,18 @@ }, "dependencies": { "abi-decoder": "2.4.0", - "bitcore-client": "^10.10.6", - "bitcore-lib": "^10.10.5", + "bitcore-client": "^10.10.7", + "bitcore-lib": "^10.10.7", "bitcore-lib-cash": "^10.10.5", "bitcore-lib-doge": "^10.10.5", "bitcore-lib-ltc": "^10.10.5", - "bitcore-p2p": "^10.10.5", - "bitcore-p2p-cash": "^10.10.5", + "bitcore-p2p": "^10.10.7", + "bitcore-p2p-cash": "^10.10.7", "bitcore-p2p-doge": "^10.10.5", - "bitcore-wallet-client": "^10.10.6", + "bitcore-wallet-client": "^10.10.7", "cors": "2.8.4", "crypto-rpc": "https://github.com/bitpay/crypto-rpc.git#370b201c98f4616b378290dc30486df5c1c769e0", - "crypto-wallet-core": "^10.10.6", + "crypto-wallet-core": "^10.10.7", "express": "4.19.2", "lodash": "4.17.11", "mongodb": "3.6.12", diff --git a/packages/bitcore-p2p-cash/package-lock.json b/packages/bitcore-p2p-cash/package-lock.json index e708f97342b..67768449699 100644 --- a/packages/bitcore-p2p-cash/package-lock.json +++ b/packages/bitcore-p2p-cash/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-p2p-cash", - "version": "10.10.5", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-p2p-cash", - "version": "10.10.5", + "version": "10.10.7", "license": "MIT", "dependencies": { "bloom-filter": "^0.2.0", diff --git a/packages/bitcore-p2p-cash/package.json b/packages/bitcore-p2p-cash/package.json index e3feff0f09a..ab88b91f207 100644 --- a/packages/bitcore-p2p-cash/package.json +++ b/packages/bitcore-p2p-cash/package.json @@ -1,6 +1,6 @@ { "name": "bitcore-p2p-cash", - "version": "10.10.5", + "version": "10.10.7", "description": "Interface to the bitcoin P2P network for bitcore", "author": "BitPay ", "main": "index.js", @@ -60,7 +60,7 @@ "url": "https://github.com/bitpay/bitcore-p2p.git" }, "dependencies": { - "bitcore-lib": "^10.10.5", + "bitcore-lib": "^10.10.7", "bitcore-lib-cash": "^10.10.5", "bloom-filter": "^0.2.0", "buffers": "bitpay/node-buffers#v0.1.2-bitpay", diff --git a/packages/bitcore-p2p/package-lock.json b/packages/bitcore-p2p/package-lock.json index c9980091bda..37c0066eb0b 100644 --- a/packages/bitcore-p2p/package-lock.json +++ b/packages/bitcore-p2p/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-p2p", - "version": "10.10.5", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-p2p", - "version": "10.10.5", + "version": "10.10.7", "license": "MIT", "dependencies": { "bloom-filter": "^0.2.0", diff --git a/packages/bitcore-p2p/package.json b/packages/bitcore-p2p/package.json index b80f6e158c4..69cd0e84720 100644 --- a/packages/bitcore-p2p/package.json +++ b/packages/bitcore-p2p/package.json @@ -1,6 +1,6 @@ { "name": "bitcore-p2p", - "version": "10.10.5", + "version": "10.10.7", "description": "Interface to the bitcoin P2P network for bitcore", "main": "index.js", "scripts": { @@ -19,7 +19,7 @@ "url": "https://github.com/bitpay/bitcore-p2p.git" }, "dependencies": { - "bitcore-lib": "^10.10.5", + "bitcore-lib": "^10.10.7", "bloom-filter": "^0.2.0", "buffers": "bitpay/node-buffers#v0.1.2-bitpay", "socks5-client": "^0.3.6" diff --git a/packages/bitcore-tss/package-lock.json b/packages/bitcore-tss/package-lock.json index 5e0c3b869e2..e0c498afd17 100644 --- a/packages/bitcore-tss/package-lock.json +++ b/packages/bitcore-tss/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-tss", - "version": "10.10.6", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-tss", - "version": "10.10.6", + "version": "10.10.7", "license": "MIT", "dependencies": { "@bitgo/sdk-lib-mpc": "^10.1.2" diff --git a/packages/bitcore-tss/package.json b/packages/bitcore-tss/package.json index 43127536183..681e3efdd24 100644 --- a/packages/bitcore-tss/package.json +++ b/packages/bitcore-tss/package.json @@ -1,6 +1,6 @@ { "name": "bitcore-tss", - "version": "10.10.6", + "version": "10.10.7", "description": "Bitcore Library for Threshold Signature Schemes", "main": "index.js", "type": "commonjs", @@ -22,7 +22,7 @@ "license": "MIT", "dependencies": { "@bitgo/sdk-lib-mpc": "^10.1.2", - "bitcore-lib": "^10.10.5" + "bitcore-lib": "^10.10.7" }, "devDependencies": { "@wdio/browser-runner": "^9.12.0", @@ -31,7 +31,7 @@ "@wdio/spec-reporter": "^9.11.0", "assert": "^2.1.0", "browserify": "^17.0.1", - "crypto-wallet-core": "^10.10.6", + "crypto-wallet-core": "^10.10.7", "mocha": "11.1.0", "webdriverio": "^9.12.0", "webpack": "^5.98.0", diff --git a/packages/bitcore-wallet-client/package-lock.json b/packages/bitcore-wallet-client/package-lock.json index 284a7823ebf..83bcc0b89cd 100644 --- a/packages/bitcore-wallet-client/package-lock.json +++ b/packages/bitcore-wallet-client/package-lock.json @@ -1,19 +1,19 @@ { "name": "bitcore-wallet-client", - "version": "10.10.6", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet-client", - "version": "10.10.6", + "version": "10.10.7", "license": "MIT", "dependencies": { "ajv": "6.12.0", "async": "0.9.2", "bip38": "1.4.0", - "bitcore-mnemonic": "^10.10.5", - "crypto-wallet-core": "^10.10.6", + "bitcore-mnemonic": "^10.10.7", + "crypto-wallet-core": "^10.10.7", "json-stable-stringify": "1.0.1", "preconditions": "2.2.3", "sjcl": "1.0.3", @@ -25,7 +25,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.6", + "bitcore-wallet-service": "^10.10.7", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-client/package.json b/packages/bitcore-wallet-client/package.json index 2210dcafaff..3bb4eb4ee2f 100644 --- a/packages/bitcore-wallet-client/package.json +++ b/packages/bitcore-wallet-client/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-client", "description": "Client for bitcore-wallet-service", "author": "BitPay Inc", - "version": "10.10.6", + "version": "10.10.7", "license": "MIT", "main": "ts_build/index.js", "types": "ts_build/index.d.js", @@ -30,8 +30,8 @@ "ajv": "6.12.0", "async": "0.9.2", "bip38": "1.4.0", - "bitcore-mnemonic": "^10.10.5", - "crypto-wallet-core": "^10.10.6", + "bitcore-mnemonic": "^10.10.7", + "crypto-wallet-core": "^10.10.7", "json-stable-stringify": "1.0.1", "preconditions": "2.2.3", "sjcl": "1.0.3", @@ -43,7 +43,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.6", + "bitcore-wallet-service": "^10.10.7", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-service/package-lock.json b/packages/bitcore-wallet-service/package-lock.json index 1bf4e117329..02345a69036 100644 --- a/packages/bitcore-wallet-service/package-lock.json +++ b/packages/bitcore-wallet-service/package-lock.json @@ -1,24 +1,24 @@ { "name": "bitcore-wallet-service", - "version": "10.10.6", + "version": "10.10.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bitcore-wallet-service", - "version": "10.10.6", + "version": "10.10.7", "license": "MIT", "dependencies": { "@sendgrid/mail": "6.5.4", "abi-decoder": "2.4.0", "async": "0.9.2", - "bitcore-lib": "^10.10.5", + "bitcore-lib": "^10.10.7", "bitcore-lib-cash": "^10.10.5", "bitcore-lib-doge": "^10.10.5", "bitcore-lib-ltc": "^10.10.5", "compression": "1.7.4", "cors": "2.8.5", - "crypto-wallet-core": "^10.10.6", + "crypto-wallet-core": "^10.10.7", "email-validator": "1.2.3", "express": "4.19.2", "express-rate-limit": "2.14.2", diff --git a/packages/bitcore-wallet-service/package.json b/packages/bitcore-wallet-service/package.json index 9a764033e56..8510ac28002 100644 --- a/packages/bitcore-wallet-service/package.json +++ b/packages/bitcore-wallet-service/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-service", "description": "A service for Mutisig HD Bitcoin Wallets", "author": "BitPay Inc", - "version": "10.10.6", + "version": "10.10.7", "license": "MIT", "main": "ts_build/index.js", "types": "./ts_build/index.d.ts", @@ -44,13 +44,13 @@ "@sendgrid/mail": "6.5.4", "abi-decoder": "2.4.0", "async": "0.9.2", - "bitcore-lib": "^10.10.5", + "bitcore-lib": "^10.10.7", "bitcore-lib-cash": "^10.10.5", "bitcore-lib-doge": "^10.10.5", "bitcore-lib-ltc": "^10.10.5", "compression": "1.7.4", "cors": "2.8.5", - "crypto-wallet-core": "^10.10.6", + "crypto-wallet-core": "^10.10.7", "email-validator": "1.2.3", "express": "4.19.2", "express-rate-limit": "2.14.2", diff --git a/packages/bitcore-wallet/package-lock.json b/packages/bitcore-wallet/package-lock.json index 2c5532d965b..df28b57d98f 100644 --- a/packages/bitcore-wallet/package-lock.json +++ b/packages/bitcore-wallet/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet", - "version": "10.10.6", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet", - "version": "10.10.6", + "version": "10.10.7", "dependencies": { "async": "^2.5.0", "commander": "^2.6.0", diff --git a/packages/bitcore-wallet/package.json b/packages/bitcore-wallet/package.json index 5c3aa92fd69..4d53b475c71 100644 --- a/packages/bitcore-wallet/package.json +++ b/packages/bitcore-wallet/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet", "description": "A CLI Mutisig HD Bitcoin Wallet, demo for Bitcore Wallet Service", "author": "BitPay Inc", - "version": "10.10.6", + "version": "10.10.7", "private": true, "keywords": [ "bitcoin", @@ -19,8 +19,8 @@ }, "dependencies": { "async": "^2.5.0", - "bitcore-lib": "^10.10.5", - "bitcore-wallet-client": "^10.10.6", + "bitcore-lib": "^10.10.7", + "bitcore-wallet-client": "^10.10.7", "commander": "^2.6.0", "lodash": "^3.3.1", "moment": "^2.9.0", diff --git a/packages/crypto-wallet-core/package-lock.json b/packages/crypto-wallet-core/package-lock.json index 806e17cdfd2..10f69740cf0 100644 --- a/packages/crypto-wallet-core/package-lock.json +++ b/packages/crypto-wallet-core/package-lock.json @@ -1,12 +1,12 @@ { "name": "crypto-wallet-core", - "version": "10.10.6", + "version": "10.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "crypto-wallet-core", - "version": "10.10.6", + "version": "10.10.7", "license": "MIT", "dependencies": { "@solana-program/compute-budget": "^0.7.0", @@ -14,7 +14,7 @@ "@solana-program/system": "^0.7.0", "@solana-program/token": "^0.5.1", "@solana/kit": "^2.1.0", - "bitcore-lib": "^10.10.5", + "bitcore-lib": "^10.10.7", "bitcore-lib-cash": "^10.10.5", "bitcore-lib-doge": "^10.10.5", "bitcore-lib-ltc": "^10.10.5", diff --git a/packages/crypto-wallet-core/package.json b/packages/crypto-wallet-core/package.json index dc9540a31bf..faac1e1de51 100644 --- a/packages/crypto-wallet-core/package.json +++ b/packages/crypto-wallet-core/package.json @@ -1,6 +1,6 @@ { "name": "crypto-wallet-core", - "version": "10.10.6", + "version": "10.10.7", "description": "A multi-currency support library for address derivation, private key creation, and transaction creation", "main": "./ts_build/src/index.js", "types": "./ts_build/src/index.d.ts", @@ -28,7 +28,7 @@ "@solana-program/system": "^0.7.0", "@solana-program/token": "^0.5.1", "@solana/kit": "^2.1.0", - "bitcore-lib": "^10.10.5", + "bitcore-lib": "^10.10.7", "bitcore-lib-cash": "^10.10.5", "bitcore-lib-doge": "^10.10.5", "bitcore-lib-ltc": "^10.10.5", From c5506ee9f83e1dc3446b2ed6e365f132bf9ae450 Mon Sep 17 00:00:00 2001 From: Mike Hume Date: Tue, 15 Jul 2025 13:52:52 -0400 Subject: [PATCH 11/57] feat(templates): removes horizontal rule from email --- .../bitcore-wallet-service/templates/en/new_outgoing_tx.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/bitcore-wallet-service/templates/en/new_outgoing_tx.html b/packages/bitcore-wallet-service/templates/en/new_outgoing_tx.html index 55d2944cda1..21717f3f497 100644 --- a/packages/bitcore-wallet-service/templates/en/new_outgoing_tx.html +++ b/packages/bitcore-wallet-service/templates/en/new_outgoing_tx.html @@ -6,8 +6,6 @@

{{title}}

-
-
A Payment of {{amount}} has been sent from your wallet. -
\ No newline at end of file +
From 9a891a602b17497e4eb2b09e2b88577883c014ec Mon Sep 17 00:00:00 2001 From: Gabriel Date: Mon, 21 Jul 2025 11:30:12 -0300 Subject: [PATCH 12/57] [FIX] select inputs when replaceTxByFee - reuse one original input --- .../src/lib/chain/btc/index.ts | 22 ++++++--- .../bitcore-wallet-service/test/chain/btc.js | 45 +++++++++++++++++++ 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/packages/bitcore-wallet-service/src/lib/chain/btc/index.ts b/packages/bitcore-wallet-service/src/lib/chain/btc/index.ts index b0475028755..5c89101acac 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/btc/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/btc/index.ts @@ -605,7 +605,11 @@ export class BtcChain implements IChain { }); }; - const select = (utxos, coin, cb) => { + const select = (utxos, requiredInputs, cb) => { + let requiredTxids = []; + if (requiredInputs.length > 0 && txp.replaceTxByFee) { + requiredTxids = _.map(requiredInputs, 'txid'); + } let totalValueInUtxos = _.sumBy(utxos, 'satoshis'); if (totalValueInUtxos < txpAmount) { logger.debug( @@ -620,6 +624,7 @@ export class BtcChain implements IChain { // remove utxos not economically worth to send utxos = _.filter(utxos, utxo => { + if (requiredTxids.includes(utxo.txid)) return true; if (utxo.satoshis <= feePerInput) return false; return true; }); @@ -657,10 +662,15 @@ export class BtcChain implements IChain { return utxo.satoshis > bigInputThreshold; }); - const bigInputs = _.sortBy(partitions[0], 'satoshis'); - const smallInputs = _.sortBy(partitions[1], utxo => { - return -utxo.satoshis; - }); + + const bigInputs = _.sortBy(partitions[0], [ + utxo => !requiredTxids.includes(utxo.txid), + 'satoshis' + ]); + const smallInputs = _.sortBy(partitions[1], [ + utxo => !requiredTxids.includes(utxo.txid), + utxo => -utxo.satoshis + ]); logger.debug('Considering ' + bigInputs.length + ' big inputs (' + Utils.formatUtxos(bigInputs) + ')'); logger.debug('Considering ' + smallInputs.length + ' small inputs (' + Utils.formatUtxos(smallInputs) + ')'); @@ -861,7 +871,7 @@ export class BtcChain implements IChain { lastGroupLength = candidateUtxos.length; - select(candidateUtxos, txp.coin, (err, selectedInputs, selectedFee) => { + select(candidateUtxos, txp.inputs, (err, selectedInputs, selectedFee) => { if (err) { // logger.debug('No inputs selected on this group: ', err); selectionError = err; diff --git a/packages/bitcore-wallet-service/test/chain/btc.js b/packages/bitcore-wallet-service/test/chain/btc.js index 2574f4801b5..c788091f1ce 100644 --- a/packages/bitcore-wallet-service/test/chain/btc.js +++ b/packages/bitcore-wallet-service/test/chain/btc.js @@ -11,6 +11,7 @@ const { BtcChain } = require('../../ts_build/lib/chain/btc'); const { TxProposal } = require('../../ts_build/lib/model/txproposal'); const { Common } = require('../../ts_build/lib/common'); +const helpers = require('../integration/helpers'); const Constants = Common.Constants; const segWitToAddress = 'BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4'; //'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'; @@ -208,6 +209,50 @@ describe('Chain BTC', function() { }); + describe('#selectTxInputs', function() { + let wallet; + let server; + let utxos; + let btc; + + before(function(done) { + helpers.before(function(res) { + btc = new BtcChain(); + done(); + }); + }); + + beforeEach(function(done) { + helpers.beforeEach(function() { + helpers.createAndJoinWallet(2, 3, {}, function(s, w) { + wallet = w; + server = s; + helpers.stubUtxos(server, wallet, [1, 2, 3, 4, 5], {}, function(u) { + utxos = u; + done(); + }); + }); + }); + }); + + after(helpers.after); + + it('should not filter out provided inputs', function(done) { + const txp = TxProposal.fromObj(aTXP()); + txp.walletId = wallet.id; + // set inputs as 2 largest utxos since selectTxInputs will spend smaller inputs first + const inputs = utxos.sort((a, b) => a.satoshis - b.satoshis).slice(-2); + txp.inputs = inputs; + txp.replaceTxByFee = true; + txp.feePerKb = 10000; // 10 sats/byte + btc.selectTxInputs(server, txp, wallet, {}, function(err) { + const txpOutpoints = txp.inputs.map(i => `${i.txid}:${i.vout}`); + const expectedOutpoints = inputs.map(i => `${i.txid}:${i.vout}`); + txpOutpoints.some(outpoint => expectedOutpoints.includes(outpoint)).should.equal(true, 'Expected resulting txp to include some provided inputs'); + done(); + }); + }); + }); }); From f77b0837eddd92fc2bd89855f7c634e715a8d9ae Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Tue, 15 Jul 2025 03:43:19 +0900 Subject: [PATCH 13/57] removed unused lodash imports --- packages/bitcore-client/src/encryption.ts | 1 - packages/bitcore-node/src/modules/index.ts | 2 -- packages/bitcore-node/src/modules/ripple/models/transaction.ts | 1 - packages/bitcore-node/test/integration/ethereum/csp.spec.ts | 1 - packages/bitcore-node/test/integration/matic/csp.spec.ts | 1 - packages/bitcore-node/test/verification/db-verify-headers.ts | 1 - packages/bitcore-wallet-service/src/bcmonitor/bcmonitor.ts | 1 - .../bitcore-wallet-service/src/externalServices/changelly.ts | 1 - packages/bitcore-wallet-service/src/externalServices/moonpay.ts | 1 - packages/bitcore-wallet-service/src/externalServices/oneInch.ts | 1 - packages/bitcore-wallet-service/src/externalServices/ramp.ts | 1 - packages/bitcore-wallet-service/src/externalServices/simplex.ts | 1 - .../bitcore-wallet-service/src/externalServices/thorswap.ts | 1 - packages/bitcore-wallet-service/src/externalServices/wyre.ts | 1 - packages/bitcore-wallet-service/src/lib/model/session.ts | 1 - 15 files changed, 16 deletions(-) diff --git a/packages/bitcore-client/src/encryption.ts b/packages/bitcore-client/src/encryption.ts index 0145acb128a..bd4ca3556ad 100644 --- a/packages/bitcore-client/src/encryption.ts +++ b/packages/bitcore-client/src/encryption.ts @@ -19,7 +19,6 @@ export function shaHash(data, algo = 'sha256') { const SHA512 = data => shaHash(data, 'sha512'); const SHA256 = data => shaHash(data, 'sha256'); const algo = 'aes-256-cbc'; -const _ = require('lodash'); export function encryptEncryptionKey(encryptionKey, password) { const password_hash = Buffer.from(SHA512(password)); diff --git a/packages/bitcore-node/src/modules/index.ts b/packages/bitcore-node/src/modules/index.ts index 00c6682611d..6649d9f82f4 100644 --- a/packages/bitcore-node/src/modules/index.ts +++ b/packages/bitcore-node/src/modules/index.ts @@ -1,5 +1,3 @@ -import _ from 'lodash'; - import logger from '../logger'; import { ChainStateProvider } from '../providers/chain-state'; import { Libs } from '../providers/libs'; diff --git a/packages/bitcore-node/src/modules/ripple/models/transaction.ts b/packages/bitcore-node/src/modules/ripple/models/transaction.ts index 420c0db14ac..68b7759b162 100644 --- a/packages/bitcore-node/src/modules/ripple/models/transaction.ts +++ b/packages/bitcore-node/src/modules/ripple/models/transaction.ts @@ -1,5 +1,4 @@ import { ObjectID } from 'bson'; -import * as _ from 'lodash'; import { LoggifyClass } from '../../../decorators/Loggify'; import logger from '../../../logger'; import { MongoBound } from '../../../models/base'; diff --git a/packages/bitcore-node/test/integration/ethereum/csp.spec.ts b/packages/bitcore-node/test/integration/ethereum/csp.spec.ts index e46570c0347..10e7ffb085a 100644 --- a/packages/bitcore-node/test/integration/ethereum/csp.spec.ts +++ b/packages/bitcore-node/test/integration/ethereum/csp.spec.ts @@ -1,7 +1,6 @@ import { ObjectId } from 'bson'; import { expect } from 'chai'; import { Request, Response } from 'express-serve-static-core'; -import _ from 'lodash'; import * as sinon from 'sinon'; import { Transform, Writable } from 'stream'; import Web3 from 'web3'; diff --git a/packages/bitcore-node/test/integration/matic/csp.spec.ts b/packages/bitcore-node/test/integration/matic/csp.spec.ts index 75feb05fa94..581cedad1bf 100644 --- a/packages/bitcore-node/test/integration/matic/csp.spec.ts +++ b/packages/bitcore-node/test/integration/matic/csp.spec.ts @@ -1,7 +1,6 @@ import { ObjectId } from 'bson'; import { expect } from 'chai'; import { Request, Response } from 'express-serve-static-core'; -import _ from 'lodash'; import * as sinon from 'sinon'; import { Transform, Writable } from 'stream'; import Web3 from 'web3'; diff --git a/packages/bitcore-node/test/verification/db-verify-headers.ts b/packages/bitcore-node/test/verification/db-verify-headers.ts index 71ce799ee48..b491fb3ef1c 100644 --- a/packages/bitcore-node/test/verification/db-verify-headers.ts +++ b/packages/bitcore-node/test/verification/db-verify-headers.ts @@ -1,6 +1,5 @@ #!/usr/bin/env node -import * as _ from 'lodash'; import { IBlock } from '../../src/types/Block'; import { BitcoinBlockStorage } from '../../src/models/block'; import { Modules } from '../../src/modules'; diff --git a/packages/bitcore-wallet-service/src/bcmonitor/bcmonitor.ts b/packages/bitcore-wallet-service/src/bcmonitor/bcmonitor.ts index bb853d99c64..05ebc50a8c3 100644 --- a/packages/bitcore-wallet-service/src/bcmonitor/bcmonitor.ts +++ b/packages/bitcore-wallet-service/src/bcmonitor/bcmonitor.ts @@ -1,5 +1,4 @@ #!/usr/bin/env node -import _ from 'lodash'; import config from '../config'; import { BlockchainMonitor } from '../lib/blockchainmonitor'; import logger from '../lib/logger'; diff --git a/packages/bitcore-wallet-service/src/externalServices/changelly.ts b/packages/bitcore-wallet-service/src/externalServices/changelly.ts index 445c5ba3d3f..ae9b77d3eaf 100644 --- a/packages/bitcore-wallet-service/src/externalServices/changelly.ts +++ b/packages/bitcore-wallet-service/src/externalServices/changelly.ts @@ -1,5 +1,4 @@ import * as crypto from 'crypto'; -import * as _ from 'lodash'; import * as request from 'request'; import config from '../config'; import { ClientError } from '../lib/errors/clienterror'; diff --git a/packages/bitcore-wallet-service/src/externalServices/moonpay.ts b/packages/bitcore-wallet-service/src/externalServices/moonpay.ts index 10827a444fe..d5d1e63bcda 100644 --- a/packages/bitcore-wallet-service/src/externalServices/moonpay.ts +++ b/packages/bitcore-wallet-service/src/externalServices/moonpay.ts @@ -1,4 +1,3 @@ -import * as _ from 'lodash'; import * as request from 'request'; import config from '../config'; import { ClientError } from '../lib/errors/clienterror'; diff --git a/packages/bitcore-wallet-service/src/externalServices/oneInch.ts b/packages/bitcore-wallet-service/src/externalServices/oneInch.ts index 34e05ff7225..aba8defdffc 100644 --- a/packages/bitcore-wallet-service/src/externalServices/oneInch.ts +++ b/packages/bitcore-wallet-service/src/externalServices/oneInch.ts @@ -1,7 +1,6 @@ import { Constants as ConstantsCWC, } from 'crypto-wallet-core'; -import * as _ from 'lodash'; import * as request from 'request'; import config from '../config'; import { Defaults } from '../lib/common/defaults'; diff --git a/packages/bitcore-wallet-service/src/externalServices/ramp.ts b/packages/bitcore-wallet-service/src/externalServices/ramp.ts index c38ca985dbd..3825768b8d9 100644 --- a/packages/bitcore-wallet-service/src/externalServices/ramp.ts +++ b/packages/bitcore-wallet-service/src/externalServices/ramp.ts @@ -1,4 +1,3 @@ -import * as _ from 'lodash'; import * as request from 'request'; import config from '../config'; import { Utils } from '../lib/common/utils'; diff --git a/packages/bitcore-wallet-service/src/externalServices/simplex.ts b/packages/bitcore-wallet-service/src/externalServices/simplex.ts index 680c89d816d..cc1d60bc128 100644 --- a/packages/bitcore-wallet-service/src/externalServices/simplex.ts +++ b/packages/bitcore-wallet-service/src/externalServices/simplex.ts @@ -1,4 +1,3 @@ -import * as _ from 'lodash'; import * as request from 'request'; import config from '../config'; import { Utils } from '../lib/common/utils'; diff --git a/packages/bitcore-wallet-service/src/externalServices/thorswap.ts b/packages/bitcore-wallet-service/src/externalServices/thorswap.ts index 0f8ff41b110..012deb2c893 100644 --- a/packages/bitcore-wallet-service/src/externalServices/thorswap.ts +++ b/packages/bitcore-wallet-service/src/externalServices/thorswap.ts @@ -1,4 +1,3 @@ -import * as _ from 'lodash'; import * as request from 'request'; import config from '../config'; import { ClientError } from '../lib/errors/clienterror'; diff --git a/packages/bitcore-wallet-service/src/externalServices/wyre.ts b/packages/bitcore-wallet-service/src/externalServices/wyre.ts index 24cdf4e6d61..235315e64e4 100644 --- a/packages/bitcore-wallet-service/src/externalServices/wyre.ts +++ b/packages/bitcore-wallet-service/src/externalServices/wyre.ts @@ -1,4 +1,3 @@ -import * as _ from 'lodash'; import * as request from 'request'; import config from '../config'; import { ClientError } from '../lib/errors/clienterror'; diff --git a/packages/bitcore-wallet-service/src/lib/model/session.ts b/packages/bitcore-wallet-service/src/lib/model/session.ts index 76a4f9c0376..b9cbcacf989 100644 --- a/packages/bitcore-wallet-service/src/lib/model/session.ts +++ b/packages/bitcore-wallet-service/src/lib/model/session.ts @@ -1,4 +1,3 @@ -import _ from 'lodash'; import { Common } from '../common'; const Uuid = require('uuid'); From e5dcf2822b5848edbaada27238c269e798c524e9 Mon Sep 17 00:00:00 2001 From: Justin Langston Date: Tue, 22 Jul 2025 11:21:55 -0400 Subject: [PATCH 14/57] v10.10.8 --- lerna.json | 2 +- packages/bitcore-client/package-lock.json | 4 ++-- packages/bitcore-client/package.json | 2 +- packages/bitcore-node/package-lock.json | 4 ++-- packages/bitcore-node/package.json | 6 +++--- packages/bitcore-wallet-client/package-lock.json | 6 +++--- packages/bitcore-wallet-client/package.json | 4 ++-- packages/bitcore-wallet-service/package-lock.json | 4 ++-- packages/bitcore-wallet-service/package.json | 2 +- packages/bitcore-wallet/package-lock.json | 4 ++-- packages/bitcore-wallet/package.json | 4 ++-- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lerna.json b/lerna.json index be8d63354b3..923c5e4a29c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "lerna": "2.9.1", - "version": "10.10.7", + "version": "10.10.8", "packages": ["packages/[^insight]*"] } diff --git a/packages/bitcore-client/package-lock.json b/packages/bitcore-client/package-lock.json index 5e1c8c79442..e04c762ffe2 100644 --- a/packages/bitcore-client/package-lock.json +++ b/packages/bitcore-client/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-client", - "version": "10.10.7", + "version": "10.10.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-client", - "version": "10.10.7", + "version": "10.10.8", "dependencies": { "async": "2.5.0", "bcrypt": "5.1.0", diff --git a/packages/bitcore-client/package.json b/packages/bitcore-client/package.json index 08eeb040d51..6ca778f3663 100644 --- a/packages/bitcore-client/package.json +++ b/packages/bitcore-client/package.json @@ -1,7 +1,7 @@ { "name": "bitcore-client", "description": "Wallet client for Bitcore node", - "version": "10.10.7", + "version": "10.10.8", "author": "Justin Langston ", "main": "./ts_build/src/index.js", "types": "./ts_build/src/index.d.ts", diff --git a/packages/bitcore-node/package-lock.json b/packages/bitcore-node/package-lock.json index e2506f993f1..2bc2420217d 100644 --- a/packages/bitcore-node/package-lock.json +++ b/packages/bitcore-node/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-node", - "version": "10.10.7", + "version": "10.10.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-node", - "version": "10.10.7", + "version": "10.10.8", "license": "MIT", "dependencies": { "abi-decoder": "2.4.0", diff --git a/packages/bitcore-node/package.json b/packages/bitcore-node/package.json index cb0151ac883..01ec122cd07 100644 --- a/packages/bitcore-node/package.json +++ b/packages/bitcore-node/package.json @@ -4,7 +4,7 @@ "engines": { "node": ">=18.0.0" }, - "version": "10.10.7", + "version": "10.10.8", "author": "Justin Langston ", "contributors": [ "Justin Langston ", @@ -90,7 +90,7 @@ }, "dependencies": { "abi-decoder": "2.4.0", - "bitcore-client": "^10.10.7", + "bitcore-client": "^10.10.8", "bitcore-lib": "^10.10.7", "bitcore-lib-cash": "^10.10.5", "bitcore-lib-doge": "^10.10.5", @@ -98,7 +98,7 @@ "bitcore-p2p": "^10.10.7", "bitcore-p2p-cash": "^10.10.7", "bitcore-p2p-doge": "^10.10.5", - "bitcore-wallet-client": "^10.10.7", + "bitcore-wallet-client": "^10.10.8", "cors": "2.8.4", "crypto-rpc": "https://github.com/bitpay/crypto-rpc.git#370b201c98f4616b378290dc30486df5c1c769e0", "crypto-wallet-core": "^10.10.7", diff --git a/packages/bitcore-wallet-client/package-lock.json b/packages/bitcore-wallet-client/package-lock.json index 83bcc0b89cd..be2ea21cfd4 100644 --- a/packages/bitcore-wallet-client/package-lock.json +++ b/packages/bitcore-wallet-client/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet-client", - "version": "10.10.7", + "version": "10.10.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet-client", - "version": "10.10.7", + "version": "10.10.8", "license": "MIT", "dependencies": { "ajv": "6.12.0", @@ -25,7 +25,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.7", + "bitcore-wallet-service": "^10.10.8", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-client/package.json b/packages/bitcore-wallet-client/package.json index 3bb4eb4ee2f..29bbd73ce1f 100644 --- a/packages/bitcore-wallet-client/package.json +++ b/packages/bitcore-wallet-client/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-client", "description": "Client for bitcore-wallet-service", "author": "BitPay Inc", - "version": "10.10.7", + "version": "10.10.8", "license": "MIT", "main": "ts_build/index.js", "types": "ts_build/index.d.js", @@ -43,7 +43,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.7", + "bitcore-wallet-service": "^10.10.8", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-service/package-lock.json b/packages/bitcore-wallet-service/package-lock.json index 02345a69036..ef84099f610 100644 --- a/packages/bitcore-wallet-service/package-lock.json +++ b/packages/bitcore-wallet-service/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet-service", - "version": "10.10.7", + "version": "10.10.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bitcore-wallet-service", - "version": "10.10.7", + "version": "10.10.8", "license": "MIT", "dependencies": { "@sendgrid/mail": "6.5.4", diff --git a/packages/bitcore-wallet-service/package.json b/packages/bitcore-wallet-service/package.json index 8510ac28002..b871c1c5bd0 100644 --- a/packages/bitcore-wallet-service/package.json +++ b/packages/bitcore-wallet-service/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-service", "description": "A service for Mutisig HD Bitcoin Wallets", "author": "BitPay Inc", - "version": "10.10.7", + "version": "10.10.8", "license": "MIT", "main": "ts_build/index.js", "types": "./ts_build/index.d.ts", diff --git a/packages/bitcore-wallet/package-lock.json b/packages/bitcore-wallet/package-lock.json index df28b57d98f..5e6da55826c 100644 --- a/packages/bitcore-wallet/package-lock.json +++ b/packages/bitcore-wallet/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet", - "version": "10.10.7", + "version": "10.10.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet", - "version": "10.10.7", + "version": "10.10.8", "dependencies": { "async": "^2.5.0", "commander": "^2.6.0", diff --git a/packages/bitcore-wallet/package.json b/packages/bitcore-wallet/package.json index 4d53b475c71..8bdedf1914d 100644 --- a/packages/bitcore-wallet/package.json +++ b/packages/bitcore-wallet/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet", "description": "A CLI Mutisig HD Bitcoin Wallet, demo for Bitcore Wallet Service", "author": "BitPay Inc", - "version": "10.10.7", + "version": "10.10.8", "private": true, "keywords": [ "bitcoin", @@ -20,7 +20,7 @@ "dependencies": { "async": "^2.5.0", "bitcore-lib": "^10.10.7", - "bitcore-wallet-client": "^10.10.7", + "bitcore-wallet-client": "^10.10.8", "commander": "^2.6.0", "lodash": "^3.3.1", "moment": "^2.9.0", From cd2a8ae74363ab811f36a35a1a2c614f59e5f149 Mon Sep 17 00:00:00 2001 From: lyambo Date: Fri, 25 Jul 2025 11:13:26 -0400 Subject: [PATCH 15/57] add feePerKb to Solana getFee --- .../bitcore-wallet-service/src/lib/chain/sol/index.ts | 8 +++++--- packages/bitcore-wallet-service/src/lib/server.ts | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts b/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts index 30f5605d0df..7900c865cb3 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts @@ -89,8 +89,10 @@ export class SolChain implements IChain { getFee(server, wallet, opts) { return new Promise(resolve => { - const numSignatures = opts.signatures || 1; - return resolve({ fee: 5000 * numSignatures }); + const numSignatures = opts.numSignatures || 1; + const feePerKb = 5000; // Fee per signature in lamports + const fee = feePerKb * numSignatures; + return resolve({ fee, feePerKb }); }); } @@ -176,7 +178,7 @@ export class SolChain implements IChain { server.getBalance({}, (err, balance) => { if (err) return cb(err); const { availableAmount } = balance; - const sigs = opts.signatures || 1; + const sigs = opts.numSignatures || 1; let fee = sigs * 5000 return cb(null, { utxosBelowFee: 0, diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index 042b14d2ce5..a762a746345 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -2355,7 +2355,8 @@ export class WalletService implements IWalletService { { feePerKb: opts.feePerKb, excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos, - returnInputs: true + returnInputs: true, + numSignatures: opts.numSignatures }, (err, info) => { if (err) return next(err); @@ -2574,6 +2575,7 @@ export class WalletService implements IWalletService { * @param {string} opts.memo - Optional. Solana transaction memo * @param {number} opts.decimals - Optional. Numbet of decimal of a desited token * @param {string} opts.fromAta - Optional. ATA addres of the sender (Solana) + * * @param {number} opts.numSignatures - Optional. Number of signatures required for the transation. For Solana fee calculation. * @param {Boolean} opts.refreshOnPublish - Optional. Allows publish function to refresh txp data * @returns {TxProposal} Transaction proposal. outputs address format will use the same format as inpunt. */ From 06bee9ffecebbf9a4b6d7913cf69206e2691815a Mon Sep 17 00:00:00 2001 From: lyambo Date: Fri, 25 Jul 2025 11:48:12 -0400 Subject: [PATCH 16/57] add solana base fee to defaults --- packages/bitcore-wallet-service/src/lib/chain/sol/index.ts | 4 ++-- packages/bitcore-wallet-service/src/lib/common/defaults.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts b/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts index 7900c865cb3..1e21937c066 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/sol/index.ts @@ -90,7 +90,7 @@ export class SolChain implements IChain { getFee(server, wallet, opts) { return new Promise(resolve => { const numSignatures = opts.numSignatures || 1; - const feePerKb = 5000; // Fee per signature in lamports + const feePerKb = Defaults.SOL_BASE_FEE; // Fee per signature in lamports const fee = feePerKb * numSignatures; return resolve({ fee, feePerKb }); }); @@ -179,7 +179,7 @@ export class SolChain implements IChain { if (err) return cb(err); const { availableAmount } = balance; const sigs = opts.numSignatures || 1; - let fee = sigs * 5000 + const fee = sigs * Defaults.SOL_BASE_FEE return cb(null, { utxosBelowFee: 0, amountBelowFee: 0, diff --git a/packages/bitcore-wallet-service/src/lib/common/defaults.ts b/packages/bitcore-wallet-service/src/lib/common/defaults.ts index 3c8fbcbd77a..4e1d6e4339b 100644 --- a/packages/bitcore-wallet-service/src/lib/common/defaults.ts +++ b/packages/bitcore-wallet-service/src/lib/common/defaults.ts @@ -412,6 +412,8 @@ export const Defaults = { // SOL has a non-refundable rent fee / balance MIN_SOL_BALANCE: 1002240, + SOL_BASE_FEE: 5000, + // Time to get the latest push notification subscriptions. In ms. PUSH_NOTIFICATION_SUBS_TIME: 10 * 60 * 1000, // 10 min. From 3814bee33719b1b99282896e4c18edc97063bb4c Mon Sep 17 00:00:00 2001 From: lyambo Date: Fri, 25 Jul 2025 13:54:50 -0400 Subject: [PATCH 17/57] lint --- packages/bitcore-wallet-service/src/lib/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index a762a746345..455a2872065 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -2575,7 +2575,7 @@ export class WalletService implements IWalletService { * @param {string} opts.memo - Optional. Solana transaction memo * @param {number} opts.decimals - Optional. Numbet of decimal of a desited token * @param {string} opts.fromAta - Optional. ATA addres of the sender (Solana) - * * @param {number} opts.numSignatures - Optional. Number of signatures required for the transation. For Solana fee calculation. + * @param {number} opts.numSignatures - Optional. Number of signatures required for the transation. For Solana fee calculation. * @param {Boolean} opts.refreshOnPublish - Optional. Allows publish function to refresh txp data * @returns {TxProposal} Transaction proposal. outputs address format will use the same format as inpunt. */ From 2f8941ffba584680fa642b6c3dcca2f3f4d3c6f1 Mon Sep 17 00:00:00 2001 From: Justin Langston Date: Fri, 25 Jul 2025 14:20:22 -0400 Subject: [PATCH 18/57] v10.10.9 --- lerna.json | 2 +- packages/bitcore-node/package-lock.json | 4 ++-- packages/bitcore-node/package.json | 4 ++-- packages/bitcore-wallet-client/package-lock.json | 6 +++--- packages/bitcore-wallet-client/package.json | 4 ++-- packages/bitcore-wallet-service/package-lock.json | 4 ++-- packages/bitcore-wallet-service/package.json | 2 +- packages/bitcore-wallet/package-lock.json | 4 ++-- packages/bitcore-wallet/package.json | 4 ++-- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lerna.json b/lerna.json index 923c5e4a29c..14333176f52 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "lerna": "2.9.1", - "version": "10.10.8", + "version": "10.10.9", "packages": ["packages/[^insight]*"] } diff --git a/packages/bitcore-node/package-lock.json b/packages/bitcore-node/package-lock.json index 2bc2420217d..287cc417ce6 100644 --- a/packages/bitcore-node/package-lock.json +++ b/packages/bitcore-node/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-node", - "version": "10.10.8", + "version": "10.10.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-node", - "version": "10.10.8", + "version": "10.10.9", "license": "MIT", "dependencies": { "abi-decoder": "2.4.0", diff --git a/packages/bitcore-node/package.json b/packages/bitcore-node/package.json index 01ec122cd07..cee1d6bb5a5 100644 --- a/packages/bitcore-node/package.json +++ b/packages/bitcore-node/package.json @@ -4,7 +4,7 @@ "engines": { "node": ">=18.0.0" }, - "version": "10.10.8", + "version": "10.10.9", "author": "Justin Langston ", "contributors": [ "Justin Langston ", @@ -98,7 +98,7 @@ "bitcore-p2p": "^10.10.7", "bitcore-p2p-cash": "^10.10.7", "bitcore-p2p-doge": "^10.10.5", - "bitcore-wallet-client": "^10.10.8", + "bitcore-wallet-client": "^10.10.9", "cors": "2.8.4", "crypto-rpc": "https://github.com/bitpay/crypto-rpc.git#370b201c98f4616b378290dc30486df5c1c769e0", "crypto-wallet-core": "^10.10.7", diff --git a/packages/bitcore-wallet-client/package-lock.json b/packages/bitcore-wallet-client/package-lock.json index be2ea21cfd4..64d1ee2c1a0 100644 --- a/packages/bitcore-wallet-client/package-lock.json +++ b/packages/bitcore-wallet-client/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet-client", - "version": "10.10.8", + "version": "10.10.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet-client", - "version": "10.10.8", + "version": "10.10.9", "license": "MIT", "dependencies": { "ajv": "6.12.0", @@ -25,7 +25,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.8", + "bitcore-wallet-service": "^10.10.9", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-client/package.json b/packages/bitcore-wallet-client/package.json index 29bbd73ce1f..b5a45e92165 100644 --- a/packages/bitcore-wallet-client/package.json +++ b/packages/bitcore-wallet-client/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-client", "description": "Client for bitcore-wallet-service", "author": "BitPay Inc", - "version": "10.10.8", + "version": "10.10.9", "license": "MIT", "main": "ts_build/index.js", "types": "ts_build/index.d.js", @@ -43,7 +43,7 @@ "devDependencies": { "@types/lodash": "^4.14.136", "@types/node": "22.14.1", - "bitcore-wallet-service": "^10.10.8", + "bitcore-wallet-service": "^10.10.9", "browserify": "^13.1.0", "chai": "^1.9.1", "coveralls": "^3.0.2", diff --git a/packages/bitcore-wallet-service/package-lock.json b/packages/bitcore-wallet-service/package-lock.json index ef84099f610..f97b4b571ea 100644 --- a/packages/bitcore-wallet-service/package-lock.json +++ b/packages/bitcore-wallet-service/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet-service", - "version": "10.10.8", + "version": "10.10.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bitcore-wallet-service", - "version": "10.10.8", + "version": "10.10.9", "license": "MIT", "dependencies": { "@sendgrid/mail": "6.5.4", diff --git a/packages/bitcore-wallet-service/package.json b/packages/bitcore-wallet-service/package.json index b871c1c5bd0..47e69d1807e 100644 --- a/packages/bitcore-wallet-service/package.json +++ b/packages/bitcore-wallet-service/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-service", "description": "A service for Mutisig HD Bitcoin Wallets", "author": "BitPay Inc", - "version": "10.10.8", + "version": "10.10.9", "license": "MIT", "main": "ts_build/index.js", "types": "./ts_build/index.d.ts", diff --git a/packages/bitcore-wallet/package-lock.json b/packages/bitcore-wallet/package-lock.json index 5e6da55826c..8606b63cf69 100644 --- a/packages/bitcore-wallet/package-lock.json +++ b/packages/bitcore-wallet/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitcore-wallet", - "version": "10.10.8", + "version": "10.10.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcore-wallet", - "version": "10.10.8", + "version": "10.10.9", "dependencies": { "async": "^2.5.0", "commander": "^2.6.0", diff --git a/packages/bitcore-wallet/package.json b/packages/bitcore-wallet/package.json index 8bdedf1914d..45f6e54e11d 100644 --- a/packages/bitcore-wallet/package.json +++ b/packages/bitcore-wallet/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet", "description": "A CLI Mutisig HD Bitcoin Wallet, demo for Bitcore Wallet Service", "author": "BitPay Inc", - "version": "10.10.8", + "version": "10.10.9", "private": true, "keywords": [ "bitcoin", @@ -20,7 +20,7 @@ "dependencies": { "async": "^2.5.0", "bitcore-lib": "^10.10.7", - "bitcore-wallet-client": "^10.10.8", + "bitcore-wallet-client": "^10.10.9", "commander": "^2.6.0", "lodash": "^3.3.1", "moment": "^2.9.0", From ec13fa59139ed5e1aabf9867172c67a9c80916e7 Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Mon, 28 Jul 2025 22:18:07 -0400 Subject: [PATCH 19/57] moved block list (now 10 blocks) to the right and linting --- packages/insight/src/pages/chain.tsx | 133 +++++++++++++++++---------- 1 file changed, 82 insertions(+), 51 deletions(-) diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 74524bf0075..04b02f0769b 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,63 +1,94 @@ -import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useApi } from 'src/api/api'; +import {useEffect, useState} from 'react'; +import {useDispatch} from 'react-redux'; +import {useNavigate, useParams} from 'react-router-dom'; +import {fetcher} from 'src/api/api'; import ChainHeader from 'src/components/chain-header'; -import { changeCurrency, changeNetwork } from 'src/store/app.actions'; -import { getApiRoot, getDefaultRefreshInterval, normalizeParams } from 'src/utilities/helper-methods'; -import Blocks from './blocks'; +import {changeCurrency, changeNetwork} from 'src/store/app.actions'; +import {getApiRoot, getFormattedDate, normalizeParams, sleep} from 'src/utilities/helper-methods'; +import {BlocksType} from 'src/utilities/models'; +import nProgress from 'nprogress'; +import Info from 'src/components/info'; +const getBlocksUrl = (currency: string, network: string) => { + return `${getApiRoot(currency)}/${currency}/${network}/block?limit=10`; +}; export const ChainDetails = () => { - const params = useParams<{currency: string, network: string}>() - let {currency, network} = params; + const params = useParams<{currency: string; network: string}>(); + const [blocksList, setBlocksList] = useState(); + const [error, setError] = useState(''); + let {currency, network} = params; - const navigate = useNavigate(); - const dispatch = useDispatch(); + const navigate = useNavigate(); + const dispatch = useDispatch(); - if (currency) { - const apiRoot = getApiRoot(currency); - const refreshInterval = getDefaultRefreshInterval(currency); - const {data, error} = useApi(`${apiRoot}/${currency}/mainnet/block?limit=1`, {refreshInterval}); - if (data) { - const {height, time, transactionCount, size} = data[0]; + useEffect(() => { + if (!currency || !network) return; + nProgress.start(); + const _normalizeParams = normalizeParams(currency, network); - } + currency = _normalizeParams.currency; + network = _normalizeParams.network; - } + dispatch(changeCurrency(currency)); + dispatch(changeNetwork(network)); - useEffect(() => { - if (!currency || !network) return; + Promise.all([fetcher(getBlocksUrl(currency, network)), sleep(500)]) + .then(([data]) => { + setBlocksList(data); + }) + .catch((e: any) => { + setError(e.message || 'Something went wrong. Please try again later.'); + }) + .finally(() => { + nProgress.done(); + }); + }, [currency, network]); - const normalizedParams = normalizeParams(currency, network); - currency = normalizedParams.currency; - network = normalizedParams.network; - dispatch(changeCurrency(currency)); - dispatch(changeNetwork(network)); + const gotoBlocks = async () => { + await navigate(`/${currency}/mainnet/blocks`); + }; - }, [currency, network]); - - const {data: priceDetails} = useApi(`https://bitpay.com/rates/${currency}/usd`); - const price = priceDetails?.data?.rate; + const gotoSingleBlockDetailsView = async (hash: string) => { + await navigate(`/${currency}/${network}/block/${hash}`); + }; + + return ( + <> + {error ? : null} - const gotoBlocks = async () => { - await navigate(`/${currency}/mainnet/blocks`); - } - - return ( - <> - {currency && network && } -
-
- Fee -
-
-
-
Go to full block list
-
- -
-
- - ); -} +
+ {currency && network && } +
+
Go to full block list
+ + + + + + + + { + blocksList?.length ? + ( + {blocksList.map((block: BlocksType, index: number) => { + const {height, hash, transactionCount, time, size} = block; + return ( + gotoSingleBlockDetailsView(hash)}> + + + + + + ); + })} + + ): null} +
HeightTimestampTransactionsSize
{height}{getFormattedDate(time)}{transactionCount}{size}
+
+
+ + ); +}; From aa43033b5146149250b4c0d383746ea42b58a0f2 Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Tue, 29 Jul 2025 15:16:08 -0400 Subject: [PATCH 20/57] added view blocks buton to bottom of transactions and fee data block --- packages/insight/src/components/chain-header.tsx | 15 ++------------- packages/insight/src/pages/chain.tsx | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index 85f62e39cf1..2793c5b3477 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -1,16 +1,10 @@ import {FC, useEffect, useRef} from 'react'; import {useApi} from 'src/api/api'; -import {buildTime, getApiRoot, getDefaultRefreshInterval} from 'src/utilities/helper-methods'; import { Chart as ChartJS } from 'chart.js'; import { colorCodes } from 'src/utilities/constants'; const ChainHeader: FC<{currency: string, network: string}> = ({currency, network}) => { - const apiRoot = getApiRoot(currency); - const refreshInterval = getDefaultRefreshInterval(currency); - const {data, error} = useApi(`${apiRoot}/${currency}/mainnet/block?limit=1`, {refreshInterval}); - const {height, time, transactionCount, size} = data[0]; - const {data: priceDetails} = useApi(`https://bitpay.com/rates/${currency}/usd`); const {data: priceDisplay} = useApi( `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, @@ -19,7 +13,7 @@ const ChainHeader: FC<{currency: string, network: string}> = ({currency, network const chartRef = useRef(null); const chartInstanceRef = useRef(null); - const price = priceDetails?.data?.rate; + const price = network === 'mainnet' ? priceDetails?.data?.rate : 0; const priceList = priceDisplay?.data?.[0]?.priceDisplay || []; const chartData = { @@ -68,7 +62,7 @@ const ChainHeader: FC<{currency: string, network: string}> = ({currency, network return ( -
+
{currency} {priceList.length > 0 && ( @@ -79,11 +73,6 @@ const ChainHeader: FC<{currency: string, network: string}> = ({currency, network
{price} USD - Height {height} - Mined {buildTime(time)} - Transaction {transactionCount} - Size {size} -
); diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 04b02f0769b..a5ee1dfe06e 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -8,6 +8,7 @@ import {getApiRoot, getFormattedDate, normalizeParams, sleep} from 'src/utilitie import {BlocksType} from 'src/utilities/models'; import nProgress from 'nprogress'; import Info from 'src/components/info'; +import { BitPay } from 'src/assets/styles/colors'; const getBlocksUrl = (currency: string, network: string) => { return `${getApiRoot(currency)}/${currency}/${network}/block?limit=10`; @@ -57,10 +58,16 @@ export const ChainDetails = () => { <> {error ? : null} -
- {currency && network && } -
-
Go to full block list
+
+
+
+ {currency && network && } +
+
+ Fee: xxx +
+
+
@@ -87,6 +94,7 @@ export const ChainDetails = () => { ): null}
Height
+
... View all Blocks
From 158b00802d837662aa9c4a2ed4ea1c01295253e1 Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Tue, 29 Jul 2025 16:32:34 -0400 Subject: [PATCH 21/57] replaced table with block chain --- packages/insight/src/pages/chain.tsx | 87 +++++++++++++++++----------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index a5ee1dfe06e..8821a4aa569 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,17 +1,18 @@ -import {useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {useDispatch} from 'react-redux'; import {useNavigate, useParams} from 'react-router-dom'; import {fetcher} from 'src/api/api'; import ChainHeader from 'src/components/chain-header'; import {changeCurrency, changeNetwork} from 'src/store/app.actions'; -import {getApiRoot, getFormattedDate, normalizeParams, sleep} from 'src/utilities/helper-methods'; +import {getApiRoot, normalizeParams, sleep} from 'src/utilities/helper-methods'; import {BlocksType} from 'src/utilities/models'; import nProgress from 'nprogress'; import Info from 'src/components/info'; import { BitPay } from 'src/assets/styles/colors'; +import { colorCodes } from 'src/utilities/constants'; const getBlocksUrl = (currency: string, network: string) => { - return `${getApiRoot(currency)}/${currency}/${network}/block?limit=10`; + return `${getApiRoot(currency)}/${currency}/${network}/block?limit=6`; }; export const ChainDetails = () => { @@ -54,47 +55,63 @@ export const ChainDetails = () => { await navigate(`/${currency}/${network}/block/${hash}`); }; + if (!currency || !network) + return null; + return ( <> {error ? : null}
-
+
- {currency && network && } +
-
- Fee: xxx +
+ Fee: xxx
-
- - - - - - - - { - blocksList?.length ? - ( - {blocksList.map((block: BlocksType, index: number) => { - const {height, hash, transactionCount, time, size} = block; - return ( - gotoSingleBlockDetailsView(hash)}> - - - - - - ); - })} - - ): null} -
HeightTimestampTransactionsSize
{height}{getFormattedDate(time)}{transactionCount}{size}
-
... View all Blocks
+
+
View all Blocks
+ { + blocksList?.length ? ( +
+ {blocksList.map((block: BlocksType, index: number) => { + const { height, hash, transactionCount, time, size } = block; + const milisecondsWhenMined = Date.now() - new Date(time).getTime(); + const minutesWhenMined = Math.floor(milisecondsWhenMined / 60000); + return ( + +
gotoSingleBlockDetailsView(hash)} + style={{ + border: '4px solid #333', + borderRadius: '10px', + padding: '1rem', + width: '100%', + maxWidth: '400px', + backgroundColor: 'transparent', + cursor: 'pointer', + textAlign: 'center', + boxShadow: '2px 2px 5px rgba(0,0,0,0.1)', + }} + > + +
{height}
+
{transactionCount} transactions
+
{size} bytes
+
mined {minutesWhenMined} minutes ago
+
+
+ + {index !== blocksList.length - 1 && ( +
|
+ )} +
+ ); + })} +
+ ): null}
From 09435bd33d74af355f64ae3f34556abd7b200376 Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Thu, 7 Aug 2025 12:08:30 -0400 Subject: [PATCH 22/57] added fee chart --- .../insight/src/components/chain-header.tsx | 140 +++++++++--------- packages/insight/src/pages/chain.tsx | 52 ++++++- 2 files changed, 119 insertions(+), 73 deletions(-) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index 2793c5b3477..f4fb2a9bbf0 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -1,81 +1,83 @@ import {FC, useEffect, useRef} from 'react'; import {useApi} from 'src/api/api'; -import { Chart as ChartJS } from 'chart.js'; -import { colorCodes } from 'src/utilities/constants'; +import {Chart as ChartJS} from 'chart.js'; +import {colorCodes} from 'src/utilities/constants'; +const ChainHeader: FC<{currency: string; network: string}> = ({currency, network}) => { + const {data: priceDetails} = useApi(`https://bitpay.com/rates/${currency}/usd`); + const {data: priceDisplay} = useApi( + `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, + ); -const ChainHeader: FC<{currency: string, network: string}> = ({currency, network}) => { - const {data: priceDetails} = useApi(`https://bitpay.com/rates/${currency}/usd`); - const {data: priceDisplay} = useApi( - `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, - ); + const chartRef = useRef(null); + const chartInstanceRef = useRef(null); - const chartRef = useRef(null); - const chartInstanceRef = useRef(null); - - const price = network === 'mainnet' ? priceDetails?.data?.rate : 0; - const priceList = priceDisplay?.data?.[0]?.priceDisplay || []; + const price = network === 'mainnet' ? priceDetails?.data?.rate : 0; + const priceList = priceDisplay?.data?.[0]?.priceDisplay || []; - const chartData = { - labels: priceList, - datasets: [ - { - data: priceList, - fill: false, - spanGaps: true, - borderColor: colorCodes[currency], - borderWidth: 2, - pointRadius: 0, - }, - ], - }; + const chartData = { + labels: priceList, + datasets: [ + { + data: priceList, + fill: false, + spanGaps: true, + borderColor: colorCodes[currency], + borderWidth: 2, + pointRadius: 0, + }, + ], + }; - const options = { - scales: { - x: {display: false}, - y: {display: false}, - }, - plugins: {legend: {display: false}}, - events: [], // disable default events - responsive: true, - maintainAspectRatio: false, - tension: 0.5, - }; - - useEffect(() => { - if (chartRef.current) { - if (chartInstanceRef.current) { - chartInstanceRef.current.destroy(); - } + const options = { + scales: { + x: {display: false}, + y: {display: false}, + }, + plugins: {legend: {display: false}}, + events: [], // disable default events + responsive: true, + maintainAspectRatio: false, + tension: 0.5, + }; - chartInstanceRef.current = new ChartJS(chartRef.current, { - type: 'line', - data: chartData, - options, - }); - } + useEffect(() => { + if (chartRef.current) { + if (chartInstanceRef.current) { + chartInstanceRef.current.destroy(); + } - return () => { - chartInstanceRef.current?.destroy(); - }; - }, [chartData, options]); + chartInstanceRef.current = new ChartJS(chartRef.current, { + type: 'line', + data: chartData, + options, + }); + } + return () => { + chartInstanceRef.current?.destroy(); + }; + }, [chartData, options]); - return ( -
-
- {currency} - {priceList.length > 0 && ( -
- -
- )} -
-
- {price} USD -
-
- ); -} + return ( +
+
+ {currency} + {priceList.length > 0 && ( +
+ +
+ )} +
+
+ {price} USD +
+
+ ); +}; -export default ChainHeader; \ No newline at end of file +export default ChainHeader; diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 8821a4aa569..843e970732b 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import {useDispatch} from 'react-redux'; import {useNavigate, useParams} from 'react-router-dom'; import {fetcher} from 'src/api/api'; @@ -8,8 +8,9 @@ import {getApiRoot, normalizeParams, sleep} from 'src/utilities/helper-methods'; import {BlocksType} from 'src/utilities/models'; import nProgress from 'nprogress'; import Info from 'src/components/info'; -import { BitPay } from 'src/assets/styles/colors'; -import { colorCodes } from 'src/utilities/constants'; +import {BitPay, Slate} from 'src/assets/styles/colors'; +import {colorCodes} from 'src/utilities/constants'; +import {Chart as ChartJS} from 'chart.js'; const getBlocksUrl = (currency: string, network: string) => { return `${getApiRoot(currency)}/${currency}/${network}/block?limit=6`; @@ -23,6 +24,34 @@ export const ChainDetails = () => { const navigate = useNavigate(); const dispatch = useDispatch(); + + const feeRef = useRef(null); + const feeInstanceRef = useRef(null); + + const fees = [0, 1, 7, 3, 2, 1, 5]; + const feeData = { + labels: fees, + datasets: [ + { + data: fees, + borderColor: Slate, + borderWidth: 3, + pointRadius: 0, + } + ] + }; + + const options = { + scales: { + x: {display: false}, + y: {display: true}, + }, + plugins: {legend: {display: false}}, + events: [], // disable default events + responsive: true, + maintainAspectRatio: false, + tension: 0.1, + }; useEffect(() => { if (!currency || !network) return; @@ -45,6 +74,18 @@ export const ChainDetails = () => { .finally(() => { nProgress.done(); }); + + if (feeRef.current) { + if (feeInstanceRef.current) { + feeInstanceRef.current.destroy(); + } + + feeInstanceRef.current = new ChartJS(feeRef.current, { + type: 'line', + data: feeData, + options, + }); + } }, [currency, network]); const gotoBlocks = async () => { @@ -62,7 +103,7 @@ export const ChainDetails = () => { <> {error ? : null} -
+
@@ -70,6 +111,9 @@ export const ChainDetails = () => {
Fee: xxx
+
+ +
View all Blocks
From cf287768f12e87cd5d5d9c1310ed6fca1ace552d Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Thu, 7 Aug 2025 21:17:06 -0400 Subject: [PATCH 23/57] refactored chain-details page and added block tip fee --- packages/insight/src/Routing.tsx | 4 +- .../src/assets/images/block-group-dark.svg | 6 + .../src/assets/images/block-group-light.svg | 6 + .../insight/src/components/block-sample.tsx | 73 +++++++ .../insight/src/components/chain-header.tsx | 4 +- packages/insight/src/pages/chain.tsx | 194 ++++++------------ 6 files changed, 154 insertions(+), 133 deletions(-) create mode 100644 packages/insight/src/assets/images/block-group-dark.svg create mode 100644 packages/insight/src/assets/images/block-group-light.svg create mode 100644 packages/insight/src/components/block-sample.tsx diff --git a/packages/insight/src/Routing.tsx b/packages/insight/src/Routing.tsx index 00859c9a290..c0b337ac30b 100644 --- a/packages/insight/src/Routing.tsx +++ b/packages/insight/src/Routing.tsx @@ -1,7 +1,7 @@ import React, {lazy, Suspense} from 'react'; import {Navigate, Route, Routes} from 'react-router-dom'; import Home from './pages'; -import {ChainDetails} from './pages/chain'; +import Chain from './pages/chain'; const Blocks = lazy(() => import('./pages/blocks')); const Block = lazy(() => import('./pages/block')); const TransactionHash = lazy(() => import('./pages/transaction')); @@ -13,7 +13,7 @@ function Routing() { } /> - } /> + } /> } /> } /> } /> diff --git a/packages/insight/src/assets/images/block-group-dark.svg b/packages/insight/src/assets/images/block-group-dark.svg new file mode 100644 index 00000000000..f5acc9b52bf --- /dev/null +++ b/packages/insight/src/assets/images/block-group-dark.svg @@ -0,0 +1,6 @@ + + + blocks-group-line + + + \ No newline at end of file diff --git a/packages/insight/src/assets/images/block-group-light.svg b/packages/insight/src/assets/images/block-group-light.svg new file mode 100644 index 00000000000..c1149f7e09f --- /dev/null +++ b/packages/insight/src/assets/images/block-group-light.svg @@ -0,0 +1,6 @@ + + + blocks-group-line + + + \ No newline at end of file diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx new file mode 100644 index 00000000000..b2099c9feec --- /dev/null +++ b/packages/insight/src/components/block-sample.tsx @@ -0,0 +1,73 @@ +import nProgress from 'nprogress'; +import React, {FC, useEffect, useState} from 'react'; +import {useNavigate} from 'react-router-dom'; +import {fetcher} from 'src/api/api'; +import Info from 'src/components/info'; +import {getApiRoot} from 'src/utilities/helper-methods'; +import {BlocksType} from 'src/utilities/models'; +import styled from 'styled-components'; + +const BlockChip = styled.div` + border: 4px solid ${({theme: {dark}}) => dark ? '#333' : '#ddd'}; + border-radius: 10px; + padding: 1rem; + width: 12rem; + max-width: 400px; + background-color: transparent; + cursor: pointer; + text-align: center; +`; + +const BlockSample: FC<{currency: string; network: string}> = ({currency, network}) => { + const navigate = useNavigate(); + + const [blocksList, setBlocksList] = useState(); + const [error, setError] = useState(''); + + useEffect(() => { + nProgress.start(); + Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=5`)]) + .then(([data]) => { + setBlocksList(data); + }) + .catch((e: any) => { + setError(e.message || 'Something went wrong. Please try again later.'); + }) + .finally(() => { + nProgress.done(); + }); + }, []); + + const gotoSingleBlockDetailsView = async (hash: string) => { + await navigate(`/${currency}/${network}/block/${hash}`); + }; + + if (error) return ; + if (!blocksList?.length) return null; + return ( +
+ {blocksList.map((block: BlocksType, index: number) => { + const {height, hash, transactionCount, time, size} = block; + const milisecondsWhenMined = Date.now() - new Date(time).getTime(); + const minutesWhenMined = Math.floor(milisecondsWhenMined / 60000); + return ( + + gotoSingleBlockDetailsView(hash)}> + +
{height}
+
{transactionCount} transactions
+
{size} bytes
+
mined {minutesWhenMined} minutes ago
+
+
+ {index !== blocksList.length - 1 && ( +
|
+ )} +
+ ); + })} +
+ ); +}; + +export default BlockSample; diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index f4fb2a9bbf0..f0b05a5ba0f 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -60,7 +60,7 @@ const ChainHeader: FC<{currency: string; network: string}> = ({currency, network }, [chartData, options]); return ( -
+
= ({currency, network style={{height: '100px'}} /> {priceList.length > 0 && ( -
+
)} diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 843e970732b..c07dbe562a5 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,163 +1,99 @@ -import React, {useEffect, useRef, useState} from 'react'; -import {useDispatch} from 'react-redux'; +import BlockSample from 'src/components/block-sample'; +import React, {useEffect, useState} from 'react'; +import ChainHeader from '../components/chain-header'; import {useNavigate, useParams} from 'react-router-dom'; -import {fetcher} from 'src/api/api'; -import ChainHeader from 'src/components/chain-header'; +import {useDispatch} from 'react-redux'; import {changeCurrency, changeNetwork} from 'src/store/app.actions'; -import {getApiRoot, normalizeParams, sleep} from 'src/utilities/helper-methods'; -import {BlocksType} from 'src/utilities/models'; +import {getApiRoot, normalizeParams} from 'src/utilities/helper-methods'; +import styled, { useTheme } from 'styled-components'; +import {colorCodes, size} from 'src/utilities/constants'; +import {fetcher} from 'src/api/api'; +import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg' +import BlockGroupLightSvg from '../assets/images/block-group-light.svg' import nProgress from 'nprogress'; -import Info from 'src/components/info'; -import {BitPay, Slate} from 'src/assets/styles/colors'; -import {colorCodes} from 'src/utilities/constants'; -import {Chart as ChartJS} from 'chart.js'; -const getBlocksUrl = (currency: string, network: string) => { - return `${getApiRoot(currency)}/${currency}/${network}/block?limit=6`; -}; +const HeaderDataContainer = styled.div` + width: 100%; + gap: 1rem; + display: flex; + flex-direction: column; + align-items: center; /* center on mobile by default */ -export const ChainDetails = () => { - const params = useParams<{currency: string; network: string}>(); - const [blocksList, setBlocksList] = useState(); - const [error, setError] = useState(''); - let {currency, network} = params; + @media screen and (min-width: ${size.mobileL}) { + flex-direction: row; + align-items: flex-start; + } +`; - const navigate = useNavigate(); +const BlocksLinkChip = styled.div` + display: flex; + border-radius: 10px; + font: menu; + width: 100%; + gap: 0.5rem; + padding: 0.2rem 0; + margin: 0.25rem 0; + justify-content: center; +` + +const Chain: React.FC = () => { + let {currency, network} = useParams<{currency: string; network: string}>(); const dispatch = useDispatch(); + const navigate = useNavigate(); + const [tipFee, setTipFee] = useState(); + const theme = useTheme(); - const feeRef = useRef(null); - const feeInstanceRef = useRef(null); - - const fees = [0, 1, 7, 3, 2, 1, 5]; - const feeData = { - labels: fees, - datasets: [ - { - data: fees, - borderColor: Slate, - borderWidth: 3, - pointRadius: 0, - } - ] - }; - - const options = { - scales: { - x: {display: false}, - y: {display: true}, - }, - plugins: {legend: {display: false}}, - events: [], // disable default events - responsive: true, - maintainAspectRatio: false, - tension: 0.1, - }; - useEffect(() => { if (!currency || !network) return; nProgress.start(); const _normalizeParams = normalizeParams(currency, network); - currency = _normalizeParams.currency; network = _normalizeParams.network; dispatch(changeCurrency(currency)); dispatch(changeNetwork(network)); - Promise.all([fetcher(getBlocksUrl(currency, network)), sleep(500)]) - .then(([data]) => { - setBlocksList(data); - }) - .catch((e: any) => { - setError(e.message || 'Something went wrong. Please try again later.'); - }) - .finally(() => { + Promise.all([ + fetcher(`${getApiRoot(currency)}/${currency}/${network}/block/tip/fee`) + ]) + .then(([fee]) => { + setTipFee(fee.mean.toFixed(5)); nProgress.done(); }); - - if (feeRef.current) { - if (feeInstanceRef.current) { - feeInstanceRef.current.destroy(); - } - - feeInstanceRef.current = new ChartJS(feeRef.current, { - type: 'line', - data: feeData, - options, - }); - } }, [currency, network]); const gotoBlocks = async () => { - await navigate(`/${currency}/mainnet/blocks`); + await navigate(`/${currency}/${network}/blocks`); }; - const gotoSingleBlockDetailsView = async (hash: string) => { - await navigate(`/${currency}/${network}/block/${hash}`); - }; - if (!currency || !network) - return null; + const BlockGroupIcon: React.FC = () => { + return ( + + ); + } + + if (!currency || !network) return null; return ( <> - {error ? : null} - -
-
-
- -
-
- Fee: xxx -
-
- -
+ +
+ + Tip Fee {tipFee} sats/byte
-
View all Blocks
- { - blocksList?.length ? ( -
- {blocksList.map((block: BlocksType, index: number) => { - const { height, hash, transactionCount, time, size } = block; - const milisecondsWhenMined = Date.now() - new Date(time).getTime(); - const minutesWhenMined = Math.floor(milisecondsWhenMined / 60000); - return ( - -
gotoSingleBlockDetailsView(hash)} - style={{ - border: '4px solid #333', - borderRadius: '10px', - padding: '1rem', - width: '100%', - maxWidth: '400px', - backgroundColor: 'transparent', - cursor: 'pointer', - textAlign: 'center', - boxShadow: '2px 2px 5px rgba(0,0,0,0.1)', - }} - > - -
{height}
-
{transactionCount} transactions
-
{size} bytes
-
mined {minutesWhenMined} minutes ago
-
-
- - {index !== blocksList.length - 1 && ( -
|
- )} -
- ); - })} -
- ): null} + + + View all Blocks + + +
-
+ ); -}; +} + +export default Chain; \ No newline at end of file From 554f2592bb672635f8791381e4059ce94ffe438d Mon Sep 17 00:00:00 2001 From: MicahMaphet Date: Mon, 11 Aug 2025 13:25:31 -0400 Subject: [PATCH 24/57] added median fee to block data display (WIP: removing Promise nesting with server logic) --- .../insight/src/components/block-sample.tsx | 49 ++++++++++++++----- packages/insight/src/pages/chain.tsx | 4 +- .../insight/src/utilities/helper-methods.ts | 27 +++++++++- 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index b2099c9feec..bd668ccbe7a 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -3,12 +3,12 @@ import React, {FC, useEffect, useState} from 'react'; import {useNavigate} from 'react-router-dom'; import {fetcher} from 'src/api/api'; import Info from 'src/components/info'; -import {getApiRoot} from 'src/utilities/helper-methods'; +import merge, {getApiRoot} from 'src/utilities/helper-methods'; import {BlocksType} from 'src/utilities/models'; import styled from 'styled-components'; const BlockChip = styled.div` - border: 4px solid ${({theme: {dark}}) => dark ? '#333' : '#ddd'}; + border: 4px solid ${({theme: {dark}}) => (dark ? '#333' : '#ddd')}; border-radius: 10px; padding: 1rem; width: 12rem; @@ -18,23 +18,40 @@ const BlockChip = styled.div` text-align: center; `; +type BlockAndFeeType = BlocksType & { + feeTotal: number; + mean: number; + median: number; + mode: number; +}; + const BlockSample: FC<{currency: string; network: string}> = ({currency, network}) => { const navigate = useNavigate(); - const [blocksList, setBlocksList] = useState(); + const [blocksList, setBlocksList] = useState(); const [error, setError] = useState(''); - useEffect(() => { nProgress.start(); Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=5`)]) .then(([data]) => { - setBlocksList(data); + // fetch the fee data for each block + Promise.all( + data.map((block: BlockAndFeeType) => + fetcher(`${getApiRoot(currency)}/${currency}/${network}/block/${block.hash}/fee`), + ) + ) + // combine the fee data with the block data + .then(fees => { + setBlocksList(data.map((block: BlocksType, i: number): BlockAndFeeType => + merge(block, fees[i]) + )); + }) + .finally(() => { + nProgress.done(); + }); }) .catch((e: any) => { setError(e.message || 'Something went wrong. Please try again later.'); - }) - .finally(() => { - nProgress.done(); }); }, []); @@ -46,8 +63,8 @@ const BlockSample: FC<{currency: string; network: string}> = ({currency, network if (!blocksList?.length) return null; return (
- {blocksList.map((block: BlocksType, index: number) => { - const {height, hash, transactionCount, time, size} = block; + {blocksList.map((block: BlockAndFeeType, index: number) => { + const {height, hash, transactionCount, time, median} = block; const milisecondsWhenMined = Date.now() - new Date(time).getTime(); const minutesWhenMined = Math.floor(milisecondsWhenMined / 60000); return ( @@ -55,13 +72,21 @@ const BlockSample: FC<{currency: string; network: string}> = ({currency, network gotoSingleBlockDetailsView(hash)}>
{height}
+
~{median.toFixed(4)} sats/vB
{transactionCount} transactions
-
{size} bytes
mined {minutesWhenMined} minutes ago
{index !== blocksList.length - 1 && ( -
|
+

+ | +

)} ); diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index c07dbe562a5..f8a3df9c3b2 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -57,7 +57,7 @@ const Chain: React.FC = () => { fetcher(`${getApiRoot(currency)}/${currency}/${network}/block/tip/fee`) ]) .then(([fee]) => { - setTipFee(fee.mean.toFixed(5)); + setTipFee(fee.mean); nProgress.done(); }); }, [currency, network]); @@ -81,7 +81,7 @@ const Chain: React.FC = () => {
- Tip Fee {tipFee} sats/byte + Tip Fee {tipFee?.toFixed(5)} sats/byte
diff --git a/packages/insight/src/utilities/helper-methods.ts b/packages/insight/src/utilities/helper-methods.ts index ce08c84d2ae..de5404e30a2 100644 --- a/packages/insight/src/utilities/helper-methods.ts +++ b/packages/insight/src/utilities/helper-methods.ts @@ -187,4 +187,29 @@ export const getDifficultyFromBits = (bits: number) => { const maxBody = Math.log(0x00ffff); const scaland = Math.log(256); return Math.exp(maxBody - Math.log(bits & 0x00ffffff) + scaland * (0x1d - ((bits & 0xff000000) >> 24))); -} \ No newline at end of file +} + +/** + * Merges the source object into the destination object. + * For each property in the source object: + * It sets the destination object property to the source property unless + * both properties are object and the destination object property is not an array. + * + * @param object destination object + * @param source source object + */ +export const merge = (dest: TDest, src: TSrc): TDest & TSrc => { + for (const key in src) { + const destProp = dest !== undefined ? (dest as any)[key] : undefined; + const srcProp = src[key]; + let result; + if (srcProp instanceof Object && destProp instanceof Object && !Array.isArray(destProp)) { + result = merge(destProp, srcProp); + } else { + result = srcProp; + } + (dest as any)[key] = result; + } + return dest as TDest & TSrc; +} +export default merge; \ No newline at end of file From dd837eff5056ad14bd2c0098921351f4afd37f02 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 23 Sep 2025 10:48:35 -0400 Subject: [PATCH 25/57] refactored nested promises to use feeData on block route --- .../insight/src/components/block-sample.tsx | 34 +++++++------------ packages/insight/src/pages/chain.tsx | 8 ++--- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index bd668ccbe7a..9ee8b336d69 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -18,11 +18,13 @@ const BlockChip = styled.div` text-align: center; `; -type BlockAndFeeType = BlocksType & { - feeTotal: number; - mean: number; - median: number; - mode: number; +export type BlockAndFeeType = BlocksType & { + feeData: { + feeTotal: number; + mean: number; + median: number; + mode: number; + } }; const BlockSample: FC<{currency: string; network: string}> = ({currency, network}) => { @@ -34,21 +36,10 @@ const BlockSample: FC<{currency: string; network: string}> = ({currency, network nProgress.start(); Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=5`)]) .then(([data]) => { - // fetch the fee data for each block - Promise.all( - data.map((block: BlockAndFeeType) => - fetcher(`${getApiRoot(currency)}/${currency}/${network}/block/${block.hash}/fee`), - ) - ) - // combine the fee data with the block data - .then(fees => { - setBlocksList(data.map((block: BlocksType, i: number): BlockAndFeeType => - merge(block, fees[i]) - )); - }) - .finally(() => { - nProgress.done(); - }); + setBlocksList(data); + }) + .finally(() => { + nProgress.done(); }) .catch((e: any) => { setError(e.message || 'Something went wrong. Please try again later.'); @@ -64,7 +55,8 @@ const BlockSample: FC<{currency: string; network: string}> = ({currency, network return (
{blocksList.map((block: BlockAndFeeType, index: number) => { - const {height, hash, transactionCount, time, median} = block; + const { height, hash, transactionCount, time } = block; + const median = block.feeData.median; const milisecondsWhenMined = Date.now() - new Date(time).getTime(); const minutesWhenMined = Math.floor(milisecondsWhenMined / 60000); return ( diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index f8a3df9c3b2..5ef02a6bd11 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -5,7 +5,7 @@ import {useNavigate, useParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; import {changeCurrency, changeNetwork} from 'src/store/app.actions'; import {getApiRoot, normalizeParams} from 'src/utilities/helper-methods'; -import styled, { useTheme } from 'styled-components'; +import styled, {useTheme} from 'styled-components'; import {colorCodes, size} from 'src/utilities/constants'; import {fetcher} from 'src/api/api'; import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg' @@ -54,10 +54,10 @@ const Chain: React.FC = () => { dispatch(changeNetwork(network)); Promise.all([ - fetcher(`${getApiRoot(currency)}/${currency}/${network}/block/tip/fee`) + fetcher(`${getApiRoot(currency)}/${currency}/${network}/block/tip`) ]) - .then(([fee]) => { - setTipFee(fee.mean); + .then(([block]) => { + setTipFee(block.feeData.mean); nProgress.done(); }); }, [currency, network]); From 3120ea05c5a074a7ad80119b9e2269c23bf42285 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Sat, 27 Sep 2025 19:48:36 -0400 Subject: [PATCH 26/57] added price labels to chart and aesthetic changes --- .../insight/src/components/block-sample.tsx | 2 +- .../insight/src/components/chain-header.tsx | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 9ee8b336d69..85f2d2e3ca7 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -3,7 +3,7 @@ import React, {FC, useEffect, useState} from 'react'; import {useNavigate} from 'react-router-dom'; import {fetcher} from 'src/api/api'; import Info from 'src/components/info'; -import merge, {getApiRoot} from 'src/utilities/helper-methods'; +import {getApiRoot} from 'src/utilities/helper-methods'; import {BlocksType} from 'src/utilities/models'; import styled from 'styled-components'; diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index f0b05a5ba0f..379d8ded163 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -13,7 +13,7 @@ const ChainHeader: FC<{currency: string; network: string}> = ({currency, network const chartInstanceRef = useRef(null); const price = network === 'mainnet' ? priceDetails?.data?.rate : 0; - const priceList = priceDisplay?.data?.[0]?.priceDisplay || []; + const priceList = (priceDisplay?.data?.[0]?.priceDisplay || []); const chartData = { labels: priceList, @@ -31,14 +31,22 @@ const ChainHeader: FC<{currency: string; network: string}> = ({currency, network const options = { scales: { - x: {display: false}, - y: {display: false}, + y: { + display: true, + title: { + display: true, + text: 'USD', + font: { size: 14 } + }, + beginAtZero: false + }, + x: {display: false} }, plugins: {legend: {display: false}}, - events: [], // disable default events + events: [], responsive: true, maintainAspectRatio: false, - tension: 0.5, + tension: 0.2, }; useEffect(() => { @@ -68,7 +76,7 @@ const ChainHeader: FC<{currency: string; network: string}> = ({currency, network style={{height: '100px'}} /> {priceList.length > 0 && ( -
+
)} From 8956bd647085865ad15cab00a5f7f0947a865ece Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 29 Sep 2025 13:33:43 -0400 Subject: [PATCH 27/57] removed y title from price chart --- packages/insight/src/components/chain-header.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index 379d8ded163..abb8c33b02b 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -33,12 +33,10 @@ const ChainHeader: FC<{currency: string; network: string}> = ({currency, network scales: { y: { display: true, - title: { - display: true, - text: 'USD', - font: { size: 14 } - }, - beginAtZero: false + beginAtZero: false, + ticks: { + maxTicksLimit: 4, + } }, x: {display: false} }, From cbd7484dcfa436a4a82913cf29fb6fc50021a2d6 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 29 Sep 2025 16:11:39 -0400 Subject: [PATCH 28/57] added fee data graph --- .../insight/src/components/block-sample.tsx | 27 +--- packages/insight/src/pages/chain.tsx | 122 ++++++++++++++++-- 2 files changed, 111 insertions(+), 38 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 85f2d2e3ca7..88baa8f77c0 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,9 +1,5 @@ -import nProgress from 'nprogress'; -import React, {FC, useEffect, useState} from 'react'; +import React, {FC} from 'react'; import {useNavigate} from 'react-router-dom'; -import {fetcher} from 'src/api/api'; -import Info from 'src/components/info'; -import {getApiRoot} from 'src/utilities/helper-methods'; import {BlocksType} from 'src/utilities/models'; import styled from 'styled-components'; @@ -27,30 +23,13 @@ export type BlockAndFeeType = BlocksType & { } }; -const BlockSample: FC<{currency: string; network: string}> = ({currency, network}) => { +const BlockSample: FC<{currency: string; network: string, blocksList: BlockAndFeeType[]}> = ({currency, network, blocksList}) => { const navigate = useNavigate(); - const [blocksList, setBlocksList] = useState(); - const [error, setError] = useState(''); - useEffect(() => { - nProgress.start(); - Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=5`)]) - .then(([data]) => { - setBlocksList(data); - }) - .finally(() => { - nProgress.done(); - }) - .catch((e: any) => { - setError(e.message || 'Something went wrong. Please try again later.'); - }); - }, []); - const gotoSingleBlockDetailsView = async (hash: string) => { await navigate(`/${currency}/${network}/block/${hash}`); }; - if (error) return ; if (!blocksList?.length) return null; return (
@@ -64,7 +43,7 @@ const BlockSample: FC<{currency: string; network: string}> = ({currency, network gotoSingleBlockDetailsView(hash)}>
{height}
-
~{median.toFixed(4)} sats/vB
+
~{median?.toFixed(4)} sats/Byte
{transactionCount} transactions
mined {minutesWhenMined} minutes ago
diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 5ef02a6bd11..2b3f1a4a202 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,5 +1,5 @@ -import BlockSample from 'src/components/block-sample'; -import React, {useEffect, useState} from 'react'; +import BlockSample, { BlockAndFeeType } from 'src/components/block-sample'; +import React, {useEffect, useRef, useState} from 'react'; import ChainHeader from '../components/chain-header'; import {useNavigate, useParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; @@ -11,6 +11,7 @@ import {fetcher} from 'src/api/api'; import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg' import BlockGroupLightSvg from '../assets/images/block-group-light.svg' import nProgress from 'nprogress'; +import {Chart as ChartJS} from 'chart.js'; const HeaderDataContainer = styled.div` width: 100%; @@ -40,27 +41,117 @@ const Chain: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); const dispatch = useDispatch(); const navigate = useNavigate(); - const [tipFee, setTipFee] = useState(); const theme = useTheme(); - + + const chartRef = useRef(null); + const chartInstanceRef = useRef(null); + const [blocksList, setBlocksList] = useState(); + const [error, setError] = useState(''); + + const chartData: { + labels: string[], + datasets: { + data: number[], + [key: string]: any + }[] + } = { + labels: [], + datasets: [ + { + data: [0], + fill: false, + spanGaps: true, + borderColor: '#555', + borderWidth: 3, + pointRadius: 0 + } + ] + }; + + const options = { + scales: { + y: { + display: true, + beginAtZero: false, + title: { + display: true, + text: 'sats/Byte', + font: { size: 14 } + }, + ticks: { + maxTicksLimit: 6 + } + }, + x: { + display: true, + ticks: { + maxTicksLimit: 10 + } + }, + }, + plugins: {legend: {display: false}}, + events: [], + responsive: true, + maintainAspectRatio: false, + tension: 0.1 + }; + useEffect(() => { - if (!currency || !network) return; nProgress.start(); + if (!currency || !network) + return; + Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=50 `)]) + .then(([data]) => { + setBlocksList(data); + }) + .finally(() => { + nProgress.done(); + }) + .catch((e: any) => { + setError(e.message || 'Something went wrong. Please try again later.'); + }); + }, []); + + useEffect(() => { + if (!currency || !network) return; const _normalizeParams = normalizeParams(currency, network); currency = _normalizeParams.currency; network = _normalizeParams.network; dispatch(changeCurrency(currency)); dispatch(changeNetwork(network)); + }, [currency, network]); - Promise.all([ - fetcher(`${getApiRoot(currency)}/${currency}/${network}/block/tip`) - ]) - .then(([block]) => { - setTipFee(block.feeData.mean); - nProgress.done(); + useEffect(() => { + if (chartRef.current) { + if (!blocksList) + return; + if (chartInstanceRef.current) { + chartInstanceRef.current.destroy(); + } + const fees = blocksList.map((block: BlockAndFeeType) => block.feeData.median).reverse(); + const dates = blocksList.map((block: BlockAndFeeType) => + new Date(block.time).toLocaleString('en-US', { + year: '2-digit', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }) + ).reverse(); + chartData.labels = dates; + chartData.datasets[0].data = fees; + chartInstanceRef.current = new ChartJS(chartRef.current, { + type: 'line', + data: chartData, + options }); - }, [currency, network]); + } + + return () => { + chartInstanceRef.current?.destroy(); + }; + }, [chartData, options, blocksList]); const gotoBlocks = async () => { await navigate(`/${currency}/${network}/blocks`); @@ -81,7 +172,9 @@ const Chain: React.FC = () => {
- Tip Fee {tipFee?.toFixed(5)} sats/byte +
+ +
@@ -89,7 +182,8 @@ const Chain: React.FC = () => { View all Blocks - + { blocksList && } +
From e1552a7a7bd270329960e10bad97f1536e3e6232 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 29 Sep 2025 17:31:04 -0400 Subject: [PATCH 29/57] started 'idle' version of block sample expand viewer --- .../insight/src/components/block-sample.tsx | 85 ++++++++++++++----- packages/insight/src/pages/chain.tsx | 37 +------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 88baa8f77c0..ad74d22a085 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,19 +1,32 @@ import React, {FC} from 'react'; import {useNavigate} from 'react-router-dom'; +import { getFormattedDate } from 'src/utilities/helper-methods'; import {BlocksType} from 'src/utilities/models'; -import styled from 'styled-components'; +import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg'; +import BlockGroupLightSvg from '../assets/images/block-group-light.svg'; +import styled, { useTheme } from 'styled-components'; +import { colorCodes } from 'src/utilities/constants'; const BlockChip = styled.div` border: 4px solid ${({theme: {dark}}) => (dark ? '#333' : '#ddd')}; border-radius: 10px; padding: 1rem; - width: 12rem; - max-width: 400px; background-color: transparent; cursor: pointer; text-align: center; `; +const BlocksLinkChip = styled.div` + display: flex; + border-radius: 15px; + font: menu; + width: 150px; + gap: 0.5rem; + padding: 0.75rem 0; + justify-content: center; + cursor: pointer; +` + export type BlockAndFeeType = BlocksType & { feeData: { feeTotal: number; @@ -25,40 +38,66 @@ export type BlockAndFeeType = BlocksType & { const BlockSample: FC<{currency: string; network: string, blocksList: BlockAndFeeType[]}> = ({currency, network, blocksList}) => { const navigate = useNavigate(); + const theme = useTheme(); const gotoSingleBlockDetailsView = async (hash: string) => { await navigate(`/${currency}/${network}/block/${hash}`); }; + const BlockGroupIcon: React.FC = () => { + return ( + + ); + } + if (!blocksList?.length) return null; return (
{blocksList.map((block: BlockAndFeeType, index: number) => { - const { height, hash, transactionCount, time } = block; + const { height, hash, transactionCount, time, size } = block; const median = block.feeData.median; - const milisecondsWhenMined = Date.now() - new Date(time).getTime(); - const minutesWhenMined = Math.floor(milisecondsWhenMined / 60000); return ( - gotoSingleBlockDetailsView(hash)}> - -
{height}
-
~{median?.toFixed(4)} sats/Byte
-
{transactionCount} transactions
-
mined {minutesWhenMined} minutes ago
-
-
+
+ gotoSingleBlockDetailsView(hash)}> + + {height} + + + + + + {getFormattedDate(time)} + {transactionCount} transactions + {size / 1000} kB + ~{median?.toFixed(4)} sats/Byte + + +
{index !== blocksList.length - 1 && ( -

- | -

+
+
+
)} + ); })} diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 2b3f1a4a202..44495d8d2f9 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -6,10 +6,8 @@ import {useDispatch} from 'react-redux'; import {changeCurrency, changeNetwork} from 'src/store/app.actions'; import {getApiRoot, normalizeParams} from 'src/utilities/helper-methods'; import styled, {useTheme} from 'styled-components'; -import {colorCodes, size} from 'src/utilities/constants'; +import {size} from 'src/utilities/constants'; import {fetcher} from 'src/api/api'; -import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg' -import BlockGroupLightSvg from '../assets/images/block-group-light.svg' import nProgress from 'nprogress'; import {Chart as ChartJS} from 'chart.js'; @@ -26,22 +24,9 @@ const HeaderDataContainer = styled.div` } `; -const BlocksLinkChip = styled.div` - display: flex; - border-radius: 10px; - font: menu; - width: 100%; - gap: 0.5rem; - padding: 0.2rem 0; - margin: 0.25rem 0; - justify-content: center; -` - const Chain: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); const dispatch = useDispatch(); - const navigate = useNavigate(); - const theme = useTheme(); const chartRef = useRef(null); const chartInstanceRef = useRef(null); @@ -153,18 +138,6 @@ const Chain: React.FC = () => { }; }, [chartData, options, blocksList]); - const gotoBlocks = async () => { - await navigate(`/${currency}/${network}/blocks`); - }; - - - const BlockGroupIcon: React.FC = () => { - return ( - - ); - } - if (!currency || !network) return null; return ( @@ -175,15 +148,9 @@ const Chain: React.FC = () => {
+ { blocksList && }
- - - View all Blocks - - - { blocksList && } -
From 0feaabbc14e480126a090e435cb77787df1eca98 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Thu, 2 Oct 2025 12:46:21 -0400 Subject: [PATCH 30/57] added data expansion to the currency block rows --- .../insight/src/components/block-details.tsx | 4 +- .../insight/src/components/block-sample.tsx | 103 ++++++++++++------ packages/insight/src/components/data-box.tsx | 6 +- packages/insight/src/pages/chain.tsx | 18 +-- packages/insight/src/utilities/models.ts | 15 +++ 5 files changed, 101 insertions(+), 45 deletions(-) diff --git a/packages/insight/src/components/block-details.tsx b/packages/insight/src/components/block-details.tsx index 1c3036db88b..5c8d3210974 100644 --- a/packages/insight/src/components/block-details.tsx +++ b/packages/insight/src/components/block-details.tsx @@ -184,9 +184,7 @@ const BlockDetails: FC = ({currency, network, block}) => { diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index ad74d22a085..97187fd748c 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,19 +1,18 @@ -import React, {FC} from 'react'; -import {useNavigate} from 'react-router-dom'; -import { getFormattedDate } from 'src/utilities/helper-methods'; -import {BlocksType} from 'src/utilities/models'; +import React, {FC, useState} from 'react'; +import {getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; +import {BitcoinBlockType} from 'src/utilities/models'; import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg'; import BlockGroupLightSvg from '../assets/images/block-group-light.svg'; -import styled, { useTheme } from 'styled-components'; -import { colorCodes } from 'src/utilities/constants'; +import styled, {useTheme} from 'styled-components'; +import {colorCodes} from 'src/utilities/constants'; +import DataBox from './data-box'; const BlockChip = styled.div` border: 4px solid ${({theme: {dark}}) => (dark ? '#333' : '#ddd')}; border-radius: 10px; padding: 1rem; background-color: transparent; - cursor: pointer; - text-align: center; + width: 100%; `; const BlocksLinkChip = styled.div` @@ -27,23 +26,9 @@ const BlocksLinkChip = styled.div` cursor: pointer; ` -export type BlockAndFeeType = BlocksType & { - feeData: { - feeTotal: number; - mean: number; - median: number; - mode: number; - } -}; - -const BlockSample: FC<{currency: string; network: string, blocksList: BlockAndFeeType[]}> = ({currency, network, blocksList}) => { - const navigate = useNavigate(); +const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => { const theme = useTheme(); - const gotoSingleBlockDetailsView = async (hash: string) => { - await navigate(`/${currency}/${network}/block/${hash}`); - }; - const BlockGroupIcon: React.FC = () => { return ( ([]); + if (!blocksList?.length) return null; return (
- {blocksList.map((block: BlockAndFeeType, index: number) => { - const { height, hash, transactionCount, time, size } = block; - const median = block.feeData.median; + {blocksList.map((block: BitcoinBlockType, index: number) => { + const { height, hash, transactionCount, time, size, merkleRoot, bits, version, nonce, reward } = block; + const { mean, median, mode, feeTotal } = block.feeData; + const confirmations = blocksList[0].height - height + 1; return (
- gotoSingleBlockDetailsView(hash)}> + expandedBlocks.includes(height) + ? setExpandedBlocks(expandedBlocks.filter(h => h !== height)) + : setExpandedBlocks([...expandedBlocks, height])}> {height} - - + { + expandedBlocks.includes(height) ? + + +
+
+
{getFormattedDate(time)}
+
+ {hash} + {merkleRoot} + + + + <> +
+ {transactionCount} + <>{size / 1000} + {confirmations} + {version} +
+ +
+ + <> +
+ {mean.toFixed(4)} + {median.toFixed(4)} + {mode.toFixed(4)} + {feeTotal} +
+ +
+ + <> +
+ {`${getConvertedValue(reward, currency).toFixed(3)} ${currency}`} + {getDifficultyFromBits(bits)} + {bits} + {nonce} +
+ +
+ + +
+
+
+ : + ~{median?.toFixed(4)} sats/Byte + }
{index !== blocksList.length - 1 && (
diff --git a/packages/insight/src/components/data-box.tsx b/packages/insight/src/components/data-box.tsx index eec3695db14..375fae477e9 100644 --- a/packages/insight/src/components/data-box.tsx +++ b/packages/insight/src/components/data-box.tsx @@ -1,7 +1,7 @@ -import {Children, FC, ReactNode} from 'react'; +import {Children, CSSProperties, FC, ReactNode} from 'react'; import {useTheme} from 'styled-components'; -const DataBox: FC<{children: ReactNode, label: string, style?: object}> = ({children, label, style}) => { +const DataBox: FC<{children: ReactNode, label?: string, style?: CSSProperties}> = ({children, label, style}) => { const theme = useTheme(); const modifiedChildren = typeof children === 'object' ? Children.map(children as JSX.Element, (child: JSX.Element) => { @@ -21,7 +21,7 @@ const DataBox: FC<{children: ReactNode, label: string, style?: object}> = ({chil margin: '0.7rem 0.2rem', ...style }}> - {label} + { label && {label} } {modifiedChildren} ); diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 44495d8d2f9..aac887a9a14 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,15 +1,17 @@ -import BlockSample, { BlockAndFeeType } from 'src/components/block-sample'; +import BlockSample from 'src/components/block-sample'; import React, {useEffect, useRef, useState} from 'react'; import ChainHeader from '../components/chain-header'; -import {useNavigate, useParams} from 'react-router-dom'; +import {useParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; import {changeCurrency, changeNetwork} from 'src/store/app.actions'; import {getApiRoot, normalizeParams} from 'src/utilities/helper-methods'; -import styled, {useTheme} from 'styled-components'; +import styled from 'styled-components'; import {size} from 'src/utilities/constants'; import {fetcher} from 'src/api/api'; import nProgress from 'nprogress'; import {Chart as ChartJS} from 'chart.js'; +import {BitcoinBlockType} from 'src/utilities/models'; +import Info from 'src/components/info'; const HeaderDataContainer = styled.div` width: 100%; @@ -30,7 +32,7 @@ const Chain: React.FC = () => { const chartRef = useRef(null); const chartInstanceRef = useRef(null); - const [blocksList, setBlocksList] = useState(); + const [blocksList, setBlocksList] = useState(); const [error, setError] = useState(''); const chartData: { @@ -114,8 +116,8 @@ const Chain: React.FC = () => { if (chartInstanceRef.current) { chartInstanceRef.current.destroy(); } - const fees = blocksList.map((block: BlockAndFeeType) => block.feeData.median).reverse(); - const dates = blocksList.map((block: BlockAndFeeType) => + const fees = blocksList.map((block: BitcoinBlockType) => block.feeData.median).reverse(); + const dates = blocksList.map((block: BitcoinBlockType) => new Date(block.time).toLocaleString('en-US', { year: '2-digit', month: '2-digit', @@ -140,15 +142,17 @@ const Chain: React.FC = () => { if (!currency || !network) return null; + return ( <> + {error ? : null}
- { blocksList && } + { blocksList && }
diff --git a/packages/insight/src/utilities/models.ts b/packages/insight/src/utilities/models.ts index 2128c1fd3e1..7c1bbc33743 100644 --- a/packages/insight/src/utilities/models.ts +++ b/packages/insight/src/utilities/models.ts @@ -109,3 +109,18 @@ export interface BlocksType { size: number; hash: string; } + +export type BitcoinBlockType = BlocksType & { + merkleRoot: string, + bits: number, + nonce: number, + reward: number, + version: number, + confirmations: number, + feeData: { + feeTotal: number; + mean: number; + median: number; + mode: number; + } +}; From edada1fc8576c94e8b4cf670525daeb2a18813fc Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Thu, 2 Oct 2025 13:33:23 -0400 Subject: [PATCH 31/57] moved currency colored chip label to top when expanded --- .../insight/src/components/block-sample.tsx | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 97187fd748c..a7fb8d77dfe 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -10,7 +10,7 @@ import DataBox from './data-box'; const BlockChip = styled.div` border: 4px solid ${({theme: {dark}}) => (dark ? '#333' : '#ddd')}; border-radius: 10px; - padding: 1rem; + padding: 0.6em; background-color: transparent; width: 100%; `; @@ -29,10 +29,10 @@ const BlocksLinkChip = styled.div` const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => { const theme = useTheme(); - const BlockGroupIcon: React.FC = () => { + const BlockGroupIcon: React.FC<{height?: string | number}> = ({height}) => { return ( + style={{height: (height ? height : '1.5rem')}}/> ); } @@ -48,21 +48,43 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu return (
- expandedBlocks.includes(height) - ? setExpandedBlocks(expandedBlocks.filter(h => h !== height)) - : setExpandedBlocks([...expandedBlocks, height])}> - - {height} - - + { + !expandedBlocks.includes(height) && + setExpandedBlocks([...expandedBlocks, height])}> + + {height} + + + } { expandedBlocks.includes(height) ?
-
-
{getFormattedDate(time)}
+
+
setExpandedBlocks(expandedBlocks.filter(h => h !== height))} + > +
+ + {height} + +
+
+ {getFormattedDate(time)} +
+
{hash} {merkleRoot} @@ -110,7 +132,6 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu flexDirection: 'row', justifyContent: 'space-between', width: '100%', - gap: '2rem' }}> {getFormattedDate(time)} {transactionCount} transactions From f5f7cb5173a63b5b200d8e6a1c58e3219206e938 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 6 Oct 2025 10:41:46 -0400 Subject: [PATCH 32/57] added sats/byte to fee data using new FeeBox element --- .../insight/src/components/block-sample.tsx | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index a7fb8d77dfe..b989f58a8bb 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -26,6 +26,28 @@ const BlocksLinkChip = styled.div` cursor: pointer; ` +const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { + return ( + + <> +
+ {value} +
+ sats + byte +
+
+ +
+ ) +} + const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => { const theme = useTheme(); @@ -103,10 +125,15 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu <>
- {mean.toFixed(4)} - {median.toFixed(4)} - {mode.toFixed(4)} - {feeTotal} + + + + + <> + {getConvertedValue(feeTotal, currency).toFixed(3)} + {currency} + +
From 8b74526c78b5a830d890d30b2ad7cb3d41122f4e Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 7 Oct 2025 13:12:52 -0400 Subject: [PATCH 33/57] replaced boxes with tables broken up into two columns for most data except maintained the box data for fee, merkle root, and hash --- .../insight/src/components/block-sample.tsx | 118 +++++++++--------- packages/insight/src/components/data-box.tsx | 15 ++- 2 files changed, 75 insertions(+), 58 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index b989f58a8bb..66b74260f4c 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -3,7 +3,7 @@ import {getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/ut import {BitcoinBlockType} from 'src/utilities/models'; import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg'; import BlockGroupLightSvg from '../assets/images/block-group-light.svg'; -import styled, {useTheme} from 'styled-components'; +import styled, {CSSProperties, useTheme} from 'styled-components'; import {colorCodes} from 'src/utilities/constants'; import DataBox from './data-box'; @@ -28,7 +28,7 @@ const BlocksLinkChip = styled.div` const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { return ( - + <>
{value} @@ -48,6 +48,15 @@ const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { ) } +const DataRow: FC<{label: string, value: any, style?: CSSProperties}> = ({label, value, style}) => { + return ( +
+ {label} + {value} +
+ ) +} + const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => { const theme = useTheme(); @@ -108,47 +117,44 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
- {hash} - {merkleRoot} - - - - <> -
- {transactionCount} - <>{size / 1000} - {confirmations} - {version} -
- -
- - <> -
- - - - - <> - {getConvertedValue(feeTotal, currency).toFixed(3)} - {currency} - - -
- -
- - <> -
- {`${getConvertedValue(reward, currency).toFixed(3)} ${currency}`} - {getDifficultyFromBits(bits)} - {bits} - {nonce} -
- -
- +
+
+ + + + +
+
+ + + + +
+ + <> +
+
+ + +
+
+ + + <> + {getConvertedValue(feeTotal, currency).toFixed(3)} + {currency} + + +
+
+ +
+
+ {hash} + {merkleRoot} +
+
@@ -170,22 +176,22 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
{index !== blocksList.length - 1 && (
-
+
)} - - + + ); })}
diff --git a/packages/insight/src/components/data-box.tsx b/packages/insight/src/components/data-box.tsx index 375fae477e9..00098bad51a 100644 --- a/packages/insight/src/components/data-box.tsx +++ b/packages/insight/src/components/data-box.tsx @@ -1,7 +1,7 @@ import {Children, CSSProperties, FC, ReactNode} from 'react'; import {useTheme} from 'styled-components'; -const DataBox: FC<{children: ReactNode, label?: string, style?: CSSProperties}> = ({children, label, style}) => { +const DataBox: FC<{children: ReactNode, label?: string, style?: CSSProperties, centerLabel?: boolean}> = ({children, label, style, centerLabel}) => { const theme = useTheme(); const modifiedChildren = typeof children === 'object' ? Children.map(children as JSX.Element, (child: JSX.Element) => { @@ -21,7 +21,18 @@ const DataBox: FC<{children: ReactNode, label?: string, style?: CSSProperties} margin: '0.7rem 0.2rem', ...style }}> - { label && {label} } + { label && + + {label} + + } {modifiedChildren} ); From 5f7cee352e4905431d03b83a9d2e2d16999ebcb5 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 7 Oct 2025 13:28:39 -0400 Subject: [PATCH 34/57] made some text not bold --- .../insight/src/components/block-sample.tsx | 129 +++++++++--------- 1 file changed, 63 insertions(+), 66 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 66b74260f4c..5eb788eb68f 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -31,16 +31,16 @@ const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { <>
- {value} + {value}
- sats - sats + byte + }}>byte
@@ -51,7 +51,7 @@ const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { const DataRow: FC<{label: string, value: any, style?: CSSProperties}> = ({label, value, style}) => { return (
- {label} + {label} {value}
) @@ -91,72 +91,69 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu { expandedBlocks.includes(height) ? - -
-
-
setExpandedBlocks(expandedBlocks.filter(h => h !== height))} - > -
- - {height} - -
-
- {getFormattedDate(time)} -
+
+
+
setExpandedBlocks(expandedBlocks.filter(h => h !== height))} + > +
+ + {height} +
-
-
- -
- - - - +
+ {getFormattedDate(time)}
-
- - - - -
- - <> -
-
- - -
-
- - - <> - {getConvertedValue(feeTotal, currency).toFixed(3)} - {currency} - - -
+
+
+
+
+ + + + +
+
+ + + + +
+ + <> +
+
+ +
- - -
- {hash} - {merkleRoot} -
+
+ + + <> + {getConvertedValue(feeTotal, currency).toFixed(3)} + {currency} + + +
+
+ +
+
+ {hash} + {merkleRoot}
-
+
: From eac648e4cf56e5300587d0c46df72be57caf990c Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 7 Oct 2025 16:48:34 -0400 Subject: [PATCH 35/57] added block chip darken on hover --- .../insight/src/components/block-sample.tsx | 35 ++++++++++--------- .../insight/src/utilities/helper-methods.ts | 17 ++++++++- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 5eb788eb68f..4feb4a2360c 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,5 +1,5 @@ import React, {FC, useState} from 'react'; -import {getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; +import {darkenHexColor, getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; import {BitcoinBlockType} from 'src/utilities/models'; import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg'; import BlockGroupLightSvg from '../assets/images/block-group-light.svg'; @@ -7,7 +7,7 @@ import styled, {CSSProperties, useTheme} from 'styled-components'; import {colorCodes} from 'src/utilities/constants'; import DataBox from './data-box'; -const BlockChip = styled.div` +const BlockDataBox = styled.div` border: 4px solid ${({theme: {dark}}) => (dark ? '#333' : '#ddd')}; border-radius: 10px; padding: 0.6em; @@ -15,7 +15,7 @@ const BlockChip = styled.div` width: 100%; `; -const BlocksLinkChip = styled.div` +const BlockChip = styled.div<{currency: string}>` display: flex; border-radius: 15px; font: menu; @@ -23,7 +23,13 @@ const BlocksLinkChip = styled.div` gap: 0.5rem; padding: 0.75rem 0; justify-content: center; + align-items: center; cursor: pointer; + background-color: ${({currency}) => colorCodes[currency]}; + + &:hover { + background-color: ${({currency}) => darkenHexColor(colorCodes[currency], 25)} + } ` const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { @@ -81,28 +87,25 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
{ !expandedBlocks.includes(height) && - setExpandedBlocks([...expandedBlocks, height])}> {height} - + } { expandedBlocks.includes(height) ? - +
-
setExpandedBlocks(expandedBlocks.filter(h => h !== height))} > @@ -114,7 +117,7 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
{getFormattedDate(time)}
-
+
@@ -154,9 +157,9 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
-
+ : - + = ({cu {size / 1000} kB ~{median?.toFixed(4)} sats/Byte - + }
{index !== blocksList.length - 1 && ( diff --git a/packages/insight/src/utilities/helper-methods.ts b/packages/insight/src/utilities/helper-methods.ts index de5404e30a2..a853fa4559a 100644 --- a/packages/insight/src/utilities/helper-methods.ts +++ b/packages/insight/src/utilities/helper-methods.ts @@ -212,4 +212,19 @@ export const merge = (dest: TDest, src: TSrc): TDest & TSrc => { } return dest as TDest & TSrc; } -export default merge; \ No newline at end of file +export default merge; + +export const darkenHexColor = (hex: string, amount: number) => { + hex = hex.replace(/^#/, ''); + + let r = parseInt(hex.substring(0, 2), 16); + let g = parseInt(hex.substring(2, 4), 16); + let b = parseInt(hex.substring(4, 6), 16); + + r = Math.max(0, r - amount); + g = Math.max(0, g - amount); + b = Math.max(0, b - amount); + + const toHex = (v: number) => v.toString(16).padStart(2, '0'); + return `#${toHex(r)}${toHex(g)}${toHex(b)}`; +} \ No newline at end of file From b392e21cb949a413f689e358a529316681120b7d Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 7 Oct 2025 17:02:11 -0400 Subject: [PATCH 36/57] incorporated background darkening into the date of the BlockChipHeader --- .../insight/src/components/block-sample.tsx | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 4feb4a2360c..ff5d6e160ba 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -31,6 +31,22 @@ const BlockChip = styled.div<{currency: string}>` background-color: ${({currency}) => darkenHexColor(colorCodes[currency], 25)} } ` +const BlockChipHeader = styled(BlockChip)` + flex-direction: column; + padding: 0.2rem 0.5rem; + border-radius: 0 0 20px 20px; + width: unset; + + .block-chip-date { + background-color: ${({currency}) => colorCodes[currency]}; + } + + &:hover { + .block-chip-date { + background-color: ${({currency}) => darkenHexColor(colorCodes[currency], 25)}; + } + } +` const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { return ( @@ -99,14 +115,8 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
- setExpandedBlocks(expandedBlocks.filter(h => h !== height))} >
@@ -114,10 +124,19 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu {height}
-
+
{getFormattedDate(time)}
- +
From 05569e49ae6851f6a80ec39c24d9720bfac97e9f Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Wed, 8 Oct 2025 12:28:08 -0400 Subject: [PATCH 37/57] added light mode --- .../insight/src/components/block-sample.tsx | 10 ++--- packages/insight/src/components/data-box.tsx | 38 +++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index ff5d6e160ba..3c2ed06d06c 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -50,7 +50,7 @@ const BlockChipHeader = styled(BlockChip)` const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { return ( - + <>
{value} @@ -151,7 +151,7 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
- + <>
@@ -160,7 +160,7 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
- + <> {getConvertedValue(feeTotal, currency).toFixed(3)} {currency} @@ -171,8 +171,8 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
- {hash} - {merkleRoot} + {hash} + {merkleRoot}
diff --git a/packages/insight/src/components/data-box.tsx b/packages/insight/src/components/data-box.tsx index 00098bad51a..3f97260a51d 100644 --- a/packages/insight/src/components/data-box.tsx +++ b/packages/insight/src/components/data-box.tsx @@ -1,26 +1,32 @@ import {Children, CSSProperties, FC, ReactNode} from 'react'; -import {useTheme} from 'styled-components'; +import styled from 'styled-components'; -const DataBox: FC<{children: ReactNode, label?: string, style?: CSSProperties, centerLabel?: boolean}> = ({children, label, style, centerLabel}) => { - const theme = useTheme(); +const DataBox: FC<{ + children: ReactNode, + label?: string, + style?: CSSProperties, + centerLabel?: boolean, + colorDark?: string, + colorLight?: string}> = ({children, label, style, centerLabel, colorDark='#5f5f5f', colorLight='#ccc'}) => { const modifiedChildren = typeof children === 'object' ? Children.map(children as JSX.Element, (child: JSX.Element) => { return ; }) : children; - + + const DataBoxFieldset = styled.fieldset` + border: 2.5px solid ${({theme: {dark}}) => dark ? colorDark : colorLight}; + border-radius: 5px; + padding: 0.1rem 0.4rem; + wordBreak: break-all; + white-space: normal; + width: fit-content; + height: fit-content; + margin: 0.7rem 0.2rem; + `; + return ( -
+ { label && } {modifiedChildren} -
+ ); } From eedb2f32450a45b002a6256600baeffb598966e2 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Wed, 8 Oct 2025 13:02:06 -0400 Subject: [PATCH 38/57] fixed bug messing up data-box children --- .../insight/src/components/block-sample.tsx | 56 +++++++++---------- packages/insight/src/components/data-box.tsx | 11 +--- .../src/components/transaction-details.tsx | 22 +++----- 3 files changed, 36 insertions(+), 53 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 3c2ed06d06c..b128aa28326 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -51,21 +51,19 @@ const BlockChipHeader = styled(BlockChip)` const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { return ( - <> -
- {value} -
- sats - byte -
+
+ {value} +
+ sats + byte
- +
) } @@ -152,23 +150,19 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu
- <> -
-
- - -
-
- - - <> - {getConvertedValue(feeTotal, currency).toFixed(3)} - {currency} - - -
+
+
+ +
- +
+ + + {getConvertedValue(feeTotal, currency).toFixed(3)} + {currency} + +
+
{hash} @@ -188,7 +182,7 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu {getFormattedDate(time)} {transactionCount} transactions {size / 1000} kB - ~{median?.toFixed(4)} sats/Byte + ~{median?.toFixed(4)} sats/byte } diff --git a/packages/insight/src/components/data-box.tsx b/packages/insight/src/components/data-box.tsx index 3f97260a51d..3ff608ae97e 100644 --- a/packages/insight/src/components/data-box.tsx +++ b/packages/insight/src/components/data-box.tsx @@ -1,4 +1,4 @@ -import {Children, CSSProperties, FC, ReactNode} from 'react'; +import {CSSProperties, FC, ReactNode} from 'react'; import styled from 'styled-components'; const DataBox: FC<{ @@ -8,17 +8,12 @@ const DataBox: FC<{ centerLabel?: boolean, colorDark?: string, colorLight?: string}> = ({children, label, style, centerLabel, colorDark='#5f5f5f', colorLight='#ccc'}) => { - const modifiedChildren = typeof children === 'object' - ? Children.map(children as JSX.Element, (child: JSX.Element) => { - return ; - }) - : children; const DataBoxFieldset = styled.fieldset` border: 2.5px solid ${({theme: {dark}}) => dark ? colorDark : colorLight}; border-radius: 5px; padding: 0.1rem 0.4rem; - wordBreak: break-all; + word-break: break-all; white-space: normal; width: fit-content; height: fit-content; @@ -39,7 +34,7 @@ const DataBox: FC<{ {label} } - {modifiedChildren} + {children} ); } diff --git a/packages/insight/src/components/transaction-details.tsx b/packages/insight/src/components/transaction-details.tsx index d970727d981..2719a87d530 100644 --- a/packages/insight/src/components/transaction-details.tsx +++ b/packages/insight/src/components/transaction-details.tsx @@ -204,27 +204,21 @@ const TransactionDetails: FC = ({ - - - goToTx(item.mintTxid, undefined, item.mintIndex) - }> - {item.mintTxid} - - + + goToTx(item.mintTxid, undefined, item.mintIndex) + }> + {item.mintTxid} +
- - {item.mintIndex} - + {item.mintIndex} {item.uiConfirmations && confirmations > 0 ? ( - - {item.uiConfirmations + confirmations} - + {item.uiConfirmations + confirmations} ) : null}
From 7ea4da1ccc2b39401bc7c3d0ee1f4551acacf246 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Thu, 30 Oct 2025 18:03:06 -0400 Subject: [PATCH 39/57] redid chain details header, put graphs next to each other, redid color scheme and added more data to header --- .../insight/src/assets/images/arrow-down.svg | 9 + .../insight/src/components/chain-header.tsx | 265 ++++++++++++++---- packages/insight/src/components/dropdown.tsx | 50 ++++ packages/insight/src/pages/chain.tsx | 94 +------ .../insight/src/utilities/helper-methods.ts | 16 ++ 5 files changed, 285 insertions(+), 149 deletions(-) create mode 100644 packages/insight/src/assets/images/arrow-down.svg create mode 100644 packages/insight/src/components/dropdown.tsx diff --git a/packages/insight/src/assets/images/arrow-down.svg b/packages/insight/src/assets/images/arrow-down.svg new file mode 100644 index 00000000000..4afe7ea0297 --- /dev/null +++ b/packages/insight/src/assets/images/arrow-down.svg @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index abb8c33b02b..ed343809e0f 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -1,86 +1,235 @@ -import {FC, useEffect, useRef} from 'react'; +import {FC, useEffect, useRef, useState} from 'react'; import {useApi} from 'src/api/api'; import {Chart as ChartJS} from 'chart.js'; import {colorCodes} from 'src/utilities/constants'; +import {BitcoinBlockType} from 'src/utilities/models'; +import styled, { useTheme } from 'styled-components'; +import { getName } from 'src/utilities/helper-methods'; +import Dropdown from './dropdown'; -const ChainHeader: FC<{currency: string; network: string}> = ({currency, network}) => { - const {data: priceDetails} = useApi(`https://bitpay.com/rates/${currency}/usd`); - const {data: priceDisplay} = useApi( - `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, - ); +const ChartTile = styled.div` + height: 400px; + width: 50%; + background-color: ${({theme: {dark}}) => dark ? '#222' : '#fff'}; + border-radius: 10px; + padding: 1.5rem; + margin: 1rem; + display: flex; + flex-direction: column; +`; + +const ChartTileHeader = styled.span` + font-size: 27px; + font-weight: bolder; +`; + +const ChainHeader: FC<{ currency: string; network: string; blocks?: BitcoinBlockType[] }> = ({ currency, network, blocks }) => { + const theme = useTheme(); + const priceDetails: { + data: { + code: string, + name: string, + rate: number + } + } = useApi(`https://bitpay.com/rates/${currency}/usd`).data; - const chartRef = useRef(null); - const chartInstanceRef = useRef(null); + const priceDisplay: { + data: Array<{ + prices: Array<{price: number, time: string}>, + currencyPair: string, + currencies: Array, + priceDisplay: Array, + percentChange: string, + priceDisplayPercentChange: string + }> + } = useApi( + `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, + ).data; const price = network === 'mainnet' ? priceDetails?.data?.rate : 0; + + const feeChartRef = useRef(null); + const feeChartInstanceRef = useRef(null); + + const priceChartRef = useRef(null); + const priceChartInstanceRef = useRef(null); const priceList = (priceDisplay?.data?.[0]?.priceDisplay || []); - const chartData = { - labels: priceList, - datasets: [ - { - data: priceList, - fill: false, - spanGaps: true, - borderColor: colorCodes[currency], - borderWidth: 2, - pointRadius: 0, - }, - ], - }; - - const options = { - scales: { - y: { - display: true, - beginAtZero: false, - ticks: { - maxTicksLimit: 4, - } - }, - x: {display: false} - }, - plugins: {legend: {display: false}}, - events: [], - responsive: true, - maintainAspectRatio: false, - tension: 0.2, - }; + const feeRanges = ['128 Blocks', '32 Blocks', '16 Blocks', '8 Blocks']; + const priceRanges = ['24 Hours', '12 Hours', '6 Hours', '3 Hours']; + + const [feeSelectedRange, setFeesSelectedRange] = useState('32 Blocks'); + const [priceSelectedRange, setPriceSelectedRange] = useState('24 Hours'); + + const [feeChangeSpan, setFeeChangeSpan] = useState(() => { return null; }); + const [priceChangeSpan, setPriceChangeSpan] = useState(() => { return null; }); useEffect(() => { - if (chartRef.current) { - if (chartInstanceRef.current) { - chartInstanceRef.current.destroy(); + if (feeChartRef.current && blocks) { + if (feeChartInstanceRef.current) { + feeChartInstanceRef.current.destroy(); } - - chartInstanceRef.current = new ChartJS(chartRef.current, { + const num = Number(feeSelectedRange.slice(0, feeSelectedRange.indexOf(' '))); + const fees = blocks.map((block: BitcoinBlockType) => block.feeData.median).reverse().slice(blocks.length - num); + const dates = blocks.map((block: BitcoinBlockType) => + new Date(block.time).toLocaleString('en-US', { + year: '2-digit', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }) + ).reverse().slice(blocks.length - num); + const chartData = { + labels: dates, + datasets: [ + { + data: fees, + fill: false, + spanGaps: true, + borderColor: colorCodes[currency], + borderWidth: 1.5, + pointRadius: 3 + } + ] + }; + const options = { + scales: { + y: { + display: true, + beginAtZero: true, + ticks: { maxTicksLimit: 6 } + }, + x: { display: false } + }, + plugins: {legend: {display: false}}, + events: [], + responsive: true, + maintainAspectRatio: false, + tension: 0 + }; + feeChartInstanceRef.current = new ChartJS(feeChartRef.current, { type: 'line', data: chartData, - options, + options + }); + + const feeChange = fees[fees.length - 1] - fees[0]; + const percentFeeChange = feeChange / fees[0] * 100; + + + setFeeChangeSpan(() => { + return + {feeChange.toFixed(2)} sats/byte ({percentFeeChange.toFixed(2)}%) + Last {feeSelectedRange} + }); } return () => { - chartInstanceRef.current?.destroy(); + feeChartInstanceRef.current?.destroy(); }; - }, [chartData, options]); + }, [blocks, feeSelectedRange, currency]); + + useEffect(() => { + const hours = Number(priceSelectedRange.slice(0, priceSelectedRange.indexOf(' '))) + const usedPrices = priceList.slice(priceList.length - hours); + const priceChartData = { + labels: usedPrices, + datasets: [ + { + data: usedPrices, + fill: false, + spanGaps: true, + borderColor: colorCodes[currency], + borderWidth: 1.5, + pointRadius: 3, + }, + ], + }; + + const priceOptions = { + scales: { + y: { + display: true, + beginAtZero: false, + ticks: { + maxTicksLimit: 4, + } + }, + x: {display: false} + }, + plugins: {legend: {display: false}}, + events: [], + responsive: true, + maintainAspectRatio: false, + tension: 0, + }; + if (priceChartRef.current) { + if (priceChartInstanceRef.current) { + priceChartInstanceRef.current.destroy(); + } + priceChartInstanceRef.current = new ChartJS(priceChartRef.current, { + type: 'line', + data: priceChartData, + options: priceOptions, + }); + } + + const priceChange = price - usedPrices[0]; + const percentPriceChange = priceChange / usedPrices[0] * 100; + + let color = 'gray'; + if (priceChange > 0) { + color = 'green'; + } else if (priceChange < 0) { + color = 'red'; + } + + setPriceChangeSpan(() => { + return + ${priceChange.toFixed(2)} ({percentPriceChange.toFixed(2)}%) + Last {priceSelectedRange} + + }); + return () => { + priceChartInstanceRef.current?.destroy(); + }; + }, [priceList, price, priceSelectedRange, currency]); return ( -
-
+
+ Blocks {currency} - {priceList.length > 0 && ( -
- -
- )} -
-
- {price} USD +
+
+ + {getName(currency)} Exchange Rate +
+ ${price} + +
+ {priceChangeSpan} +
+ +
+
+ + {getName(currency)} Fee +
+ {blocks?.at(0)?.feeData.median.toFixed(3)} sats/byte + +
+ {feeChangeSpan} +
+ +
+
+
); diff --git a/packages/insight/src/components/dropdown.tsx b/packages/insight/src/components/dropdown.tsx new file mode 100644 index 00000000000..95dd18e2b0c --- /dev/null +++ b/packages/insight/src/components/dropdown.tsx @@ -0,0 +1,50 @@ +import { FC, CSSProperties } from 'react'; +import ArrowDown from '../assets/images/arrow-down.svg'; +import { useTheme } from 'styled-components'; + +const Dropdown: FC<{ + options: string[], + value?: string, + onChange?: (value: string) => void, + style?: CSSProperties +}> = ({options, value, onChange, style}) => { + const theme = useTheme(); + return ( +
+ + Arrow Down +
+ ); +} + +export default Dropdown; \ No newline at end of file diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index aac887a9a14..e4bbead28fe 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,5 +1,5 @@ import BlockSample from 'src/components/block-sample'; -import React, {useEffect, useRef, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import ChainHeader from '../components/chain-header'; import {useParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; @@ -9,7 +9,6 @@ import styled from 'styled-components'; import {size} from 'src/utilities/constants'; import {fetcher} from 'src/api/api'; import nProgress from 'nprogress'; -import {Chart as ChartJS} from 'chart.js'; import {BitcoinBlockType} from 'src/utilities/models'; import Info from 'src/components/info'; @@ -30,64 +29,14 @@ const Chain: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); const dispatch = useDispatch(); - const chartRef = useRef(null); - const chartInstanceRef = useRef(null); const [blocksList, setBlocksList] = useState(); const [error, setError] = useState(''); - const chartData: { - labels: string[], - datasets: { - data: number[], - [key: string]: any - }[] - } = { - labels: [], - datasets: [ - { - data: [0], - fill: false, - spanGaps: true, - borderColor: '#555', - borderWidth: 3, - pointRadius: 0 - } - ] - }; - - const options = { - scales: { - y: { - display: true, - beginAtZero: false, - title: { - display: true, - text: 'sats/Byte', - font: { size: 14 } - }, - ticks: { - maxTicksLimit: 6 - } - }, - x: { - display: true, - ticks: { - maxTicksLimit: 10 - } - }, - }, - plugins: {legend: {display: false}}, - events: [], - responsive: true, - maintainAspectRatio: false, - tension: 0.1 - }; - useEffect(() => { nProgress.start(); if (!currency || !network) return; - Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=50 `)]) + Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=128`)]) .then(([data]) => { setBlocksList(data); }) @@ -109,53 +58,16 @@ const Chain: React.FC = () => { dispatch(changeNetwork(network)); }, [currency, network]); - useEffect(() => { - if (chartRef.current) { - if (!blocksList) - return; - if (chartInstanceRef.current) { - chartInstanceRef.current.destroy(); - } - const fees = blocksList.map((block: BitcoinBlockType) => block.feeData.median).reverse(); - const dates = blocksList.map((block: BitcoinBlockType) => - new Date(block.time).toLocaleString('en-US', { - year: '2-digit', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit' - }) - ).reverse(); - chartData.labels = dates; - chartData.datasets[0].data = fees; - chartInstanceRef.current = new ChartJS(chartRef.current, { - type: 'line', - data: chartData, - options - }); - } - - return () => { - chartInstanceRef.current?.destroy(); - }; - }, [chartData, options, blocksList]); - if (!currency || !network) return null; - return ( <> {error ? : null}
- -
- -
+ { blocksList && }
-
-
); diff --git a/packages/insight/src/utilities/helper-methods.ts b/packages/insight/src/utilities/helper-methods.ts index a853fa4559a..085926cbb7e 100644 --- a/packages/insight/src/utilities/helper-methods.ts +++ b/packages/insight/src/utilities/helper-methods.ts @@ -183,6 +183,22 @@ export const getLib = (currency: string) => { } }; +export const getName = (currency: string) => { + switch (currency.toUpperCase()) { + case 'BTC': + default: + return 'Bitcoin'; + case 'BCH': + return 'Bitcoin Cash'; + case 'DOGE': + return 'Doge'; + case 'LTC': + return 'Litecoin'; + case 'ETH': + return 'Ethereum'; + } +} + export const getDifficultyFromBits = (bits: number) => { const maxBody = Math.log(0x00ffff); const scaland = Math.log(256); From 71e20566d1a2553966024b3c79b2853275204ea8 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Fri, 31 Oct 2025 01:33:16 -0400 Subject: [PATCH 40/57] redid block listing making it more like a data table and numerous stylistic changes, not all data incorporated --- .../src/assets/images/block-group-dark.svg | 6 - .../src/assets/images/block-group-light.svg | 6 - packages/insight/src/assets/images/cube.svg | 3 + .../insight/src/components/block-sample.tsx | 321 +++++++----------- packages/insight/src/components/dropdown.tsx | 2 +- 5 files changed, 132 insertions(+), 206 deletions(-) delete mode 100644 packages/insight/src/assets/images/block-group-dark.svg delete mode 100644 packages/insight/src/assets/images/block-group-light.svg create mode 100644 packages/insight/src/assets/images/cube.svg diff --git a/packages/insight/src/assets/images/block-group-dark.svg b/packages/insight/src/assets/images/block-group-dark.svg deleted file mode 100644 index f5acc9b52bf..00000000000 --- a/packages/insight/src/assets/images/block-group-dark.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - blocks-group-line - - - \ No newline at end of file diff --git a/packages/insight/src/assets/images/block-group-light.svg b/packages/insight/src/assets/images/block-group-light.svg deleted file mode 100644 index c1149f7e09f..00000000000 --- a/packages/insight/src/assets/images/block-group-light.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - blocks-group-line - - - \ No newline at end of file diff --git a/packages/insight/src/assets/images/cube.svg b/packages/insight/src/assets/images/cube.svg new file mode 100644 index 00000000000..8f58c5695f4 --- /dev/null +++ b/packages/insight/src/assets/images/cube.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index b128aa28326..e5f5e9d0afa 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,213 +1,148 @@ import React, {FC, useState} from 'react'; -import {darkenHexColor, getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; +import {getFormattedDate} from 'src/utilities/helper-methods'; import {BitcoinBlockType} from 'src/utilities/models'; -import BlockGroupDarkSvg from '../assets/images/block-group-dark.svg'; -import BlockGroupLightSvg from '../assets/images/block-group-light.svg'; -import styled, {CSSProperties, useTheme} from 'styled-components'; -import {colorCodes} from 'src/utilities/constants'; -import DataBox from './data-box'; +import Cube from '../assets/images/cube.svg'; +import Arrow from '../assets/images/arrow.svg'; +import ArrowDown from '../assets/images/arrow-down.svg'; +import styled from 'styled-components'; +import { LightBlack, NeutralSlate, Slate30 } from 'src/assets/styles/colors'; +import CopyText from './copy-text'; -const BlockDataBox = styled.div` - border: 4px solid ${({theme: {dark}}) => (dark ? '#333' : '#ddd')}; - border-radius: 10px; - padding: 0.6em; - background-color: transparent; - width: 100%; -`; - -const BlockChip = styled.div<{currency: string}>` - display: flex; - border-radius: 15px; - font: menu; - width: 150px; - gap: 0.5rem; - padding: 0.75rem 0; - justify-content: center; - align-items: center; - cursor: pointer; - background-color: ${({currency}) => colorCodes[currency]}; +const BlockListTableRow = styled.tr` + text-align: center; + line-height: 45px; - &:hover { - background-color: ${({currency}) => darkenHexColor(colorCodes[currency], 25)} - } -` -const BlockChipHeader = styled(BlockChip)` - flex-direction: column; - padding: 0.2rem 0.5rem; - border-radius: 0 0 20px 20px; - width: unset; - - .block-chip-date { - background-color: ${({currency}) => colorCodes[currency]}; + &:nth-child(even) { + background-color: ${({theme: {dark}}) => (dark ? LightBlack : Slate30)}; } - &:hover { - .block-chip-date { - background-color: ${({currency}) => darkenHexColor(colorCodes[currency], 25)}; - } + &:nth-child(odd) { + background-color: ${({theme: {dark}}) => (dark ? '#090909' : NeutralSlate)}; } -` -const FeeBox: FC<{label: string, value: string}> = ({label, value}) => { - return ( - -
- {value} -
- sats - byte -
-
-
- ) -} - -const DataRow: FC<{label: string, value: any, style?: CSSProperties}> = ({label, value, style}) => { - return ( -
- {label} - {value} -
- ) -} + font-size: 16px; +`; const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => { - const theme = useTheme(); - - const BlockGroupIcon: React.FC<{height?: string | number}> = ({height}) => { - return ( - - ); - } - const [expandedBlocks, setExpandedBlocks] = useState([]); if (!blocksList?.length) return null; return ( -
- {blocksList.map((block: BitcoinBlockType, index: number) => { - const { height, hash, transactionCount, time, size, merkleRoot, bits, version, nonce, reward } = block; - const { mean, median, mode, feeTotal } = block.feeData; - const confirmations = blocksList[0].height - height + 1; - return ( - -
- { - !expandedBlocks.includes(height) && - setExpandedBlocks([...expandedBlocks, height])}> - - {height} - - - } - { - expandedBlocks.includes(height) ? - -
-
- setExpandedBlocks(expandedBlocks.filter(h => h !== height))} - > -
- - {height} - -
-
- {getFormattedDate(time)} -
-
-
-
-
- - - - -
-
- - - - -
- -
-
- - + + + + + + + + + + + + { + blocksList.map((block: BitcoinBlockType, index: number) => { + const { height, time, transactionCount, size, feeData, hash, merkleRoot, bits, nonce } = block; + const { median } = feeData; + const expanded = expandedBlocks.includes(height); + return ( + + expanded + ? setExpandedBlocks(expandedBlocks.filter(h => h !== height)) + : setExpandedBlocks([...expandedBlocks, height])}> + + + + + + + {expanded && <> + {/* Alternates the color so the data below this row stays the same*/} + + + + + + } + + ); + }) + } + +
HeightTimestampTransactionsSizeFee Rate
+ + {expanded + ? arrow + : arrow + } + cube + {height} + + {getFormattedDate(time)}{transactionCount}{size}{median.toFixed(4)} +
+
+ Block Hash +
+ {hash} + +
+
+ Merkle Root +
+ {merkleRoot} + +
-
- - - {getConvertedValue(feeTotal, currency).toFixed(3)} - {currency} - + Summary +
+
+ Previous Block +
+ {height - 1} +
+
+ Bits +
+ {bits} +
+
+ +
+ Next Block +
+ {height + 1} +
+
+ Nonce +
+ {nonce} +
+
- -
- {hash} - {merkleRoot} -
-
- - - : - - - {getFormattedDate(time)} - {transactionCount} transactions - {size / 1000} kB - ~{median?.toFixed(4)} sats/byte - - - } - - {index !== blocksList.length - 1 && ( -
-
-
- )} - - - ); - })} -
+
); }; diff --git a/packages/insight/src/components/dropdown.tsx b/packages/insight/src/components/dropdown.tsx index 95dd18e2b0c..ed46d603c5c 100644 --- a/packages/insight/src/components/dropdown.tsx +++ b/packages/insight/src/components/dropdown.tsx @@ -32,7 +32,7 @@ const Dropdown: FC<{ Arrow Down Date: Fri, 31 Oct 2025 11:17:11 -0400 Subject: [PATCH 41/57] added the rest of the data to the block expansion, refactoring with info card element --- .../insight/src/assets/images/arrow-down.svg | 2 +- packages/insight/src/components/InfoCard.tsx | 34 ++++++ .../insight/src/components/block-sample.tsx | 111 +++++++----------- packages/insight/src/components/copy-text.tsx | 7 +- 4 files changed, 81 insertions(+), 73 deletions(-) create mode 100644 packages/insight/src/components/InfoCard.tsx diff --git a/packages/insight/src/assets/images/arrow-down.svg b/packages/insight/src/assets/images/arrow-down.svg index 4afe7ea0297..0eac6df0a26 100644 --- a/packages/insight/src/assets/images/arrow-down.svg +++ b/packages/insight/src/assets/images/arrow-down.svg @@ -5,5 +5,5 @@ fill="none" xmlns="http://www.w3.org/2000/svg" > - + \ No newline at end of file diff --git a/packages/insight/src/components/InfoCard.tsx b/packages/insight/src/components/InfoCard.tsx new file mode 100644 index 00000000000..265eee956fc --- /dev/null +++ b/packages/insight/src/components/InfoCard.tsx @@ -0,0 +1,34 @@ +import { FC } from 'react'; +import CopyText from './copy-text'; + + +type InfoCardType = { + data: Array<{label: string, value: any, copyText?: boolean}>, +}; + +const InfoCard: FC = ({data}) => { + return ( +
+ {data.map((d, index) => { + const { label, value, copyText } = d; + return (<> + {label} +
+ {value} + {copyText && } +
+ { index !== data.length - 1 &&
} + ); + })} +
+ ); +} + +export default InfoCard; \ No newline at end of file diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index e5f5e9d0afa..2e1c6c130da 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,12 +1,12 @@ import React, {FC, useState} from 'react'; -import {getFormattedDate} from 'src/utilities/helper-methods'; +import {getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; import {BitcoinBlockType} from 'src/utilities/models'; import Cube from '../assets/images/cube.svg'; import Arrow from '../assets/images/arrow.svg'; import ArrowDown from '../assets/images/arrow-down.svg'; import styled from 'styled-components'; import { LightBlack, NeutralSlate, Slate30 } from 'src/assets/styles/colors'; -import CopyText from './copy-text'; +import InfoCard from './InfoCard'; const BlockListTableRow = styled.tr` text-align: center; @@ -41,96 +41,69 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu { blocksList.map((block: BitcoinBlockType, index: number) => { - const { height, time, transactionCount, size, feeData, hash, merkleRoot, bits, nonce } = block; - const { median } = feeData; - const expanded = expandedBlocks.includes(height); + const feeData = block.feeData; + const expanded = expandedBlocks.includes(block.height); return ( expanded - ? setExpandedBlocks(expandedBlocks.filter(h => h !== height)) - : setExpandedBlocks([...expandedBlocks, height])}> + ? setExpandedBlocks(expandedBlocks.filter(h => h !== block.height)) + : setExpandedBlocks([...expandedBlocks, block.height])}> {expanded - ? arrow + ? arrow : arrow } cube - {height} + {block.height} - {getFormattedDate(time)} - {transactionCount} - {size} - {median.toFixed(4)} + {getFormattedDate(block.time)} + {block.transactionCount} + {block.size} + {feeData.median.toFixed(4)} {expanded && <> {/* Alternates the color so the data below this row stays the same*/} +
-
- Block Hash -
- {hash} - -
-
- Merkle Root -
- {merkleRoot} - -
-
+ Summary
-
- Previous Block -
- {height - 1} -
-
- Bits -
- {bits} -
-
- -
- Next Block -
- {height + 1} -
-
- Nonce -
- {nonce} + + + {[{label: 'Mean', value: feeData.mean}, {label: 'Median', value: feeData.median}, {label: 'Mode', value: feeData.mode}] + .map(({label, value}, key) => { + return ( +
+ {label} + {value.toFixed(4)} +
+
) + }) + }
-
+ } + ]}/>
diff --git a/packages/insight/src/components/copy-text.tsx b/packages/insight/src/components/copy-text.tsx index 156b18c8e25..2ef40f912df 100644 --- a/packages/insight/src/components/copy-text.tsx +++ b/packages/insight/src/components/copy-text.tsx @@ -1,5 +1,5 @@ import {AnimatePresence, motion} from 'framer-motion'; -import {FC, memo, useState} from 'react'; +import {CSSProperties, FC, memo, useState} from 'react'; import {CopyToClipboard} from 'react-copy-to-clipboard'; import styled from 'styled-components'; import CopySvg from '../assets/images/copy-icon.svg'; @@ -23,8 +23,9 @@ const IconImage = styled(motion.img)` interface CopyTextProps { text: string; + style?: CSSProperties } -const CopyText: FC = ({text}) => { +const CopyText: FC = ({text, style}) => { const [copied, setCopied] = useState(false); const onClickCopy = () => { @@ -60,7 +61,7 @@ const CopyText: FC = ({text}) => { }; return ( - + {copied ? ( Date: Fri, 31 Oct 2025 12:42:52 -0400 Subject: [PATCH 42/57] improved color scheme, especially dark mode and added details in the mockup --- .../src/assets/images/arrow-down-black.svg | 9 +++++ .../insight/src/assets/images/arrow-down.svg | 2 +- .../insight/src/assets/images/arrow-thin.svg | 9 +++++ packages/insight/src/components/InfoCard.tsx | 33 ++++++++++------ .../insight/src/components/block-sample.tsx | 38 ++++++++++--------- .../insight/src/components/chain-header.tsx | 8 ++-- packages/insight/src/components/dropdown.tsx | 3 +- packages/insight/src/pages/chain.tsx | 10 ++--- 8 files changed, 70 insertions(+), 42 deletions(-) create mode 100644 packages/insight/src/assets/images/arrow-down-black.svg create mode 100644 packages/insight/src/assets/images/arrow-thin.svg diff --git a/packages/insight/src/assets/images/arrow-down-black.svg b/packages/insight/src/assets/images/arrow-down-black.svg new file mode 100644 index 00000000000..cd26e1e77a0 --- /dev/null +++ b/packages/insight/src/assets/images/arrow-down-black.svg @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/packages/insight/src/assets/images/arrow-down.svg b/packages/insight/src/assets/images/arrow-down.svg index 0eac6df0a26..7de9ca03b57 100644 --- a/packages/insight/src/assets/images/arrow-down.svg +++ b/packages/insight/src/assets/images/arrow-down.svg @@ -5,5 +5,5 @@ fill="none" xmlns="http://www.w3.org/2000/svg" > - + \ No newline at end of file diff --git a/packages/insight/src/assets/images/arrow-thin.svg b/packages/insight/src/assets/images/arrow-thin.svg new file mode 100644 index 00000000000..da9da611522 --- /dev/null +++ b/packages/insight/src/assets/images/arrow-thin.svg @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/packages/insight/src/components/InfoCard.tsx b/packages/insight/src/components/InfoCard.tsx index 265eee956fc..e99bc4f9f22 100644 --- a/packages/insight/src/components/InfoCard.tsx +++ b/packages/insight/src/components/InfoCard.tsx @@ -1,33 +1,44 @@ import { FC } from 'react'; import CopyText from './copy-text'; +import styled from 'styled-components'; type InfoCardType = { data: Array<{label: string, value: any, copyText?: boolean}>, }; +const Card = styled.div` + display: flex; + flex-direction: column; + width: 100%; + background-color: ${({theme: {dark}}) => dark ? '#222' : '#fff'}; + padding: 14px; + border-radius: 8px; +`; + +const Label = styled.span` + color: ${({theme: {dark}}) => dark ? '#888' : '#474d53'}; + align-self: flex-start; + line-height: 1.6; + margin-bottom: -2; + font-size: 18px; +`; + const InfoCard: FC = ({data}) => { return ( -
+ {data.map((d, index) => { const { label, value, copyText } = d; return (<> - {label} +
{value} {copyText && }
- { index !== data.length - 1 &&
} + { index !== data.length - 1 &&
} ); })} -
+ ); } diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 2e1c6c130da..ab4f4eb5c2b 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -2,28 +2,28 @@ import React, {FC, useState} from 'react'; import {getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; import {BitcoinBlockType} from 'src/utilities/models'; import Cube from '../assets/images/cube.svg'; -import Arrow from '../assets/images/arrow.svg'; +import Arrow from '../assets/images/arrow-thin.svg'; import ArrowDown from '../assets/images/arrow-down.svg'; -import styled from 'styled-components'; -import { LightBlack, NeutralSlate, Slate30 } from 'src/assets/styles/colors'; +import styled, { useTheme } from 'styled-components'; import InfoCard from './InfoCard'; const BlockListTableRow = styled.tr` text-align: center; line-height: 45px; - &:nth-child(even) { - background-color: ${({theme: {dark}}) => (dark ? LightBlack : Slate30)}; + &:nth-child(odd) { + background-color: ${({theme: {dark}}) => (dark ? '#2a2a2a' : '#f6f7f9')}; } - &:nth-child(odd) { - background-color: ${({theme: {dark}}) => (dark ? '#090909' : NeutralSlate)}; + &:nth-child(even) { + background-color: ${({theme: {dark}}) => (dark ? '#0f0f0f' : '#e0e4e7')}; } font-size: 16px; `; const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => { + const theme = useTheme(); const [expandedBlocks, setExpandedBlocks] = useState([]); if (!blocksList?.length) return null; @@ -31,14 +31,15 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu - - - - - + + + + + + { blocksList.map((block: BitcoinBlockType, index: number) => { const feeData = block.feeData; @@ -50,13 +51,13 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu onClick={() => expanded ? setExpandedBlocks(expandedBlocks.filter(h => h !== block.height)) : setExpandedBlocks([...expandedBlocks, block.height])}> - @@ -72,6 +73,7 @@ const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({cu + + + } + + ); + }) + } + +
HeightTimestampTransactionsSizeFee RateHeightTimestampTransactionsSizeFee Rate
+ {expanded - ? arrow - : arrow + ? arrow + : arrow } - cube + cube {block.height}
+
= ({cu {[{label: 'Mean', value: feeData.mean}, {label: 'Median', value: feeData.median}, {label: 'Mode', value: feeData.mode}] .map(({label, value}, key) => { return ( -
- {label} +
+ {label} {value.toFixed(4)}
) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index ed343809e0f..5327326e93c 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -203,14 +203,14 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: BitcoinBlock {currency} -
+
{getName(currency)} Exchange Rate
- ${price} + ${price.toLocaleString()}
{priceChangeSpan} @@ -222,7 +222,7 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: BitcoinBlock {getName(currency)} Fee
{blocks?.at(0)?.feeData.median.toFixed(3)} sats/byte - +
{feeChangeSpan}
diff --git a/packages/insight/src/components/dropdown.tsx b/packages/insight/src/components/dropdown.tsx index ed46d603c5c..7bc68250003 100644 --- a/packages/insight/src/components/dropdown.tsx +++ b/packages/insight/src/components/dropdown.tsx @@ -1,5 +1,6 @@ import { FC, CSSProperties } from 'react'; import ArrowDown from '../assets/images/arrow-down.svg'; +import ArrowDownBlack from '../assets/images/arrow-down-black.svg'; import { useTheme } from 'styled-components'; const Dropdown: FC<{ @@ -31,7 +32,7 @@ const Dropdown: FC<{ ))} Arrow Down { <> {error ? : null} -
- - { blocksList && } -
+ + { blocksList && }
); From 6e3cbf365b8d860b63bf2b2d9519bc345c5f37f5 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Fri, 31 Oct 2025 13:47:22 -0400 Subject: [PATCH 43/57] added InfiniteScroll to blocks list in chain page --- .../insight/src/components/block-sample.tsx | 216 ++++++++++-------- packages/insight/src/pages/chain.tsx | 2 +- 2 files changed, 128 insertions(+), 90 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index ab4f4eb5c2b..e75d0725867 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,11 +1,15 @@ import React, {FC, useState} from 'react'; -import {getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; +import {getApiRoot, getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; import {BitcoinBlockType} from 'src/utilities/models'; import Cube from '../assets/images/cube.svg'; import Arrow from '../assets/images/arrow-thin.svg'; import ArrowDown from '../assets/images/arrow-down.svg'; import styled, { useTheme } from 'styled-components'; import InfoCard from './InfoCard'; +import InfiniteScroll from 'react-infinite-scroll-component'; +import { fetcher } from 'src/api/api'; +import InfiniteScrollLoadSpinner from './infinite-scroll-load-spinner'; +import Info from './info'; const BlockListTableRow = styled.tr` text-align: center; @@ -22,102 +26,136 @@ const BlockListTableRow = styled.tr` font-size: 16px; `; -const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => { + +const getBlocksUrl = (currency: string, network: string) => { + return `${getApiRoot(currency)}/${currency}/${network}/block?limit=200`; +}; + +const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockType[]}> = ({currency, network, blocks}) => { const theme = useTheme(); const [expandedBlocks, setExpandedBlocks] = useState([]); + const [blocksList, setBlocksList] = useState(blocks); + const [error, setError] = useState(''); + const [hasMore, setHasMore] = useState(true); + + const fetchMore = async (_blocksList: BitcoinBlockType[]) => { + if (!_blocksList.length || !currency || !network) return; + const since = _blocksList[_blocksList.length - 1].height; + try { + const newData: [BitcoinBlockType] = await fetcher( + `${getBlocksUrl(currency, network)}&since=${since}&paging=height&direction=-1`, + ); + if (newData?.length) { + setBlocksList(_blocksList.concat(newData)); + } else { + setHasMore(false); + } + } catch (e: any) { + setError(e.message || 'Something went wrong. Please try again later.'); + } + }; if (!blocksList?.length) return null; return ( - - - - - - - - - - - - - { - blocksList.map((block: BitcoinBlockType, index: number) => { - const feeData = block.feeData; - const expanded = expandedBlocks.includes(block.height); - return ( - - expanded - ? setExpandedBlocks(expandedBlocks.filter(h => h !== block.height)) - : setExpandedBlocks([...expandedBlocks, block.height])}> - - - - - - - {expanded && <> - {/* Alternates the color so the data below this row stays the same*/} - - + <> + {error ? : null} + fetchMore(blocksList)} + hasMore={hasMore} + loader={} + dataLength={blocksList.length}> +
HeightTimestampTransactionsSizeFee Rate
- - {expanded - ? arrow - : arrow - } - cube - {block.height} - - {getFormattedDate(block.time)}{block.transactionCount}{block.size}{feeData.median.toFixed(4)}
+ + + + + + + + + + + + { + blocksList.map((block: BitcoinBlockType, index: number) => { + const feeData = block.feeData; + const expanded = expandedBlocks.includes(block.height); + return ( + + expanded + ? setExpandedBlocks(expandedBlocks.filter(h => h !== block.height)) + : setExpandedBlocks([...expandedBlocks, block.height])}> + + + + + + + {expanded && <> + {/* Alternates the color so the data below this row stays the same*/} + + - - - - } - - ); - }) - } - -
HeightTimestampTransactionsSizeFee Rate
+ + {expanded + ? arrow + : arrow + } + cube + {block.height} + + {getFormattedDate(block.time)}{block.transactionCount}{block.size}{feeData.median.toFixed(4)} -
-
- - Summary -
- - - {[{label: 'Mean', value: feeData.mean}, {label: 'Median', value: feeData.median}, {label: 'Mode', value: feeData.mode}] - .map(({label, value}, key) => { - return ( -
- {label} - {value.toFixed(4)} -
-
) - }) +
+
+
+ + Summary +
+ + + {[{label: 'Mean', value: feeData.mean}, {label: 'Median', value: feeData.median}, {label: 'Mode', value: feeData.mode}] + .map(({label, value}, key) => { + return ( +
+ {label} + {value.toFixed(4)} +
+
) + }) + } +
} + ]}/>
- } - ]}/> - - -
+
+
+ + ); }; diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index 1b779f367a0..dcb68726a5e 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -63,7 +63,7 @@ const Chain: React.FC = () => { {error ? : null} - { blocksList && } + { blocksList && } ); From dc71645a3a6034b450a2b2517d3f875a4fb89f5f Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Fri, 31 Oct 2025 14:19:25 -0400 Subject: [PATCH 44/57] added view transactions and next block links to blocks list; changed expanded on click area to height with arrow and cube icons --- .../src/assets/images/arrow-forward-blue.svg | 3 ++ .../src/assets/images/arrow-outward.svg | 3 ++ .../insight/src/components/block-sample.tsx | 35 +++++++++++++++---- 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 packages/insight/src/assets/images/arrow-forward-blue.svg create mode 100644 packages/insight/src/assets/images/arrow-outward.svg diff --git a/packages/insight/src/assets/images/arrow-forward-blue.svg b/packages/insight/src/assets/images/arrow-forward-blue.svg new file mode 100644 index 00000000000..d08d59d0f47 --- /dev/null +++ b/packages/insight/src/assets/images/arrow-forward-blue.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/insight/src/assets/images/arrow-outward.svg b/packages/insight/src/assets/images/arrow-outward.svg new file mode 100644 index 00000000000..658974e740c --- /dev/null +++ b/packages/insight/src/assets/images/arrow-outward.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index e75d0725867..99693e58462 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -3,6 +3,8 @@ import {getApiRoot, getConvertedValue, getDifficultyFromBits, getFormattedDate} import {BitcoinBlockType} from 'src/utilities/models'; import Cube from '../assets/images/cube.svg'; import Arrow from '../assets/images/arrow-thin.svg'; +import ArrowOutward from '../assets/images/arrow-outward.svg'; +import ForwardArrow from '../assets/images/arrow-forward-blue.svg'; import ArrowDown from '../assets/images/arrow-down.svg'; import styled, { useTheme } from 'styled-components'; import InfoCard from './InfoCard'; @@ -10,6 +12,7 @@ import InfiniteScroll from 'react-infinite-scroll-component'; import { fetcher } from 'src/api/api'; import InfiniteScrollLoadSpinner from './infinite-scroll-load-spinner'; import Info from './info'; +import { useNavigate } from 'react-router-dom'; const BlockListTableRow = styled.tr` text-align: center; @@ -33,6 +36,7 @@ const getBlocksUrl = (currency: string, network: string) => { const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockType[]}> = ({currency, network, blocks}) => { const theme = useTheme(); + const navigate = useNavigate(); const [expandedBlocks, setExpandedBlocks] = useState([]); const [blocksList, setBlocksList] = useState(blocks); const [error, setError] = useState(''); @@ -55,6 +59,10 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy } }; + const gotoSingleBlockDetailsView = async (hash: string) => { + await navigate(`/${currency}/${network}/block/${hash}`); + }; + if (!blocksList?.length) return null; return ( <> @@ -82,13 +90,13 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy const expanded = expandedBlocks.includes(block.height); return ( - expanded - ? setExpandedBlocks(expandedBlocks.filter(h => h !== block.height)) - : setExpandedBlocks([...expandedBlocks, block.height])}> + - + expanded + ? setExpandedBlocks(expandedBlocks.filter(h => h !== block.height)) + : setExpandedBlocks([...expandedBlocks, block.height])}> {expanded ? arrow : arrow @@ -124,7 +132,16 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy {label: 'Miner fees', value: `${getConvertedValue(feeData.feeTotal, currency).toFixed(5)} ${currency}`}, ]}/> + {block.height + 1} + gotoSingleBlockDetailsView(blocksList[index - 1].hash)} + alt='Next Block' + title={`Go to block ${block.height + 1}`} + /> + }, {label: 'Nonce', value: block.nonce}, {label: 'Confirmations', value: blocksList[0].height - block.height + 1}, {label: 'Difficulty', value: getDifficultyFromBits(block.bits).toFixed(0)}, @@ -143,6 +160,10 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy } ]}/>
+ gotoSingleBlockDetailsView(block.hash)}> + View transactions + arrow +
From 1f678b33f483b1d8decfb709479074f49c123bb5 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 4 Nov 2025 10:34:44 -0500 Subject: [PATCH 45/57] removed unnecessary container in chain page --- packages/insight/src/pages/chain.tsx | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index dcb68726a5e..a2a1869c83e 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -5,24 +5,11 @@ import {useParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; import {changeCurrency, changeNetwork} from 'src/store/app.actions'; import {getApiRoot, normalizeParams} from 'src/utilities/helper-methods'; -import styled from 'styled-components'; -import {size} from 'src/utilities/constants'; import {fetcher} from 'src/api/api'; import nProgress from 'nprogress'; import {BitcoinBlockType} from 'src/utilities/models'; import Info from 'src/components/info'; -const HeaderDataContainer = styled.div` - width: 100%; - min-width: 0; - align-items: center; /* center on mobile by default */ - - @media screen and (min-width: ${size.mobileL}) { - flex-direction: row; - align-items: flex-start; - } -`; - const Chain: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); const dispatch = useDispatch(); @@ -61,10 +48,8 @@ const Chain: React.FC = () => { return ( <> {error ? : null} - - - { blocksList && } - + + { blocksList && } ); } From 67db60791c0fb123ad0ce33ecbd32f8613462f70 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 4 Nov 2025 13:55:19 -0500 Subject: [PATCH 46/57] fixed ETH error on chain-page: handle no fee data (as well as other data) --- .../insight/src/components/block-sample.tsx | 52 ++++++++++--------- .../insight/src/components/chain-header.tsx | 35 +++++++------ packages/insight/src/utilities/models.ts | 7 ++- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-sample.tsx index 99693e58462..134823b9412 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-sample.tsx @@ -1,6 +1,6 @@ import React, {FC, useState} from 'react'; import {getApiRoot, getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; -import {BitcoinBlockType} from 'src/utilities/models'; +import {BitcoinBlockType, FeeData} from 'src/utilities/models'; import Cube from '../assets/images/cube.svg'; import Arrow from '../assets/images/arrow-thin.svg'; import ArrowOutward from '../assets/images/arrow-outward.svg'; @@ -34,13 +34,15 @@ const getBlocksUrl = (currency: string, network: string) => { return `${getApiRoot(currency)}/${currency}/${network}/block?limit=200`; }; -const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockType[]}> = ({currency, network, blocks}) => { +const BlockSample: FC<{currency: string, network: string, blocks: Array>}> = ({currency, network, blocks}) => { const theme = useTheme(); const navigate = useNavigate(); const [expandedBlocks, setExpandedBlocks] = useState([]); const [blocksList, setBlocksList] = useState(blocks); const [error, setError] = useState(''); const [hasMore, setHasMore] = useState(true); + const hasFees = blocks.every(block => block.feeData); + const columnProportion = hasFees ? '20%' : '25%'; const fetchMore = async (_blocksList: BitcoinBlockType[]) => { if (!_blocksList.length || !currency || !network) return; @@ -75,17 +77,17 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy - - - - - + + + + + {hasFees && } { - blocksList.map((block: BitcoinBlockType, index: number) => { + blocksList.map((block: BitcoinBlockType & Partial, index: number) => { const feeData = block.feeData; const expanded = expandedBlocks.includes(block.height); return ( @@ -108,7 +110,7 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy - + { feeData && } {expanded && <> {/* Alternates the color so the data below this row stays the same*/} @@ -126,10 +128,10 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy
@@ -144,20 +146,20 @@ const BlockSample: FC<{currency: string, network: string, blocks: BitcoinBlockTy }, {label: 'Nonce', value: block.nonce}, {label: 'Confirmations', value: blocksList[0].height - block.height + 1}, - {label: 'Difficulty', value: getDifficultyFromBits(block.bits).toFixed(0)}, - {label: 'Fee data', value:
- {[{label: 'Mean', value: feeData.mean}, {label: 'Median', value: feeData.median}, {label: 'Mode', value: feeData.mode}] - .map(({label, value}, key) => { - return ( -
- {label} - {value.toFixed(4)} -
-
) - }) - } + ...(block.bits ? [{label: 'Difficulty', value: getDifficultyFromBits(block.bits).toFixed(0)}] : []), + ...(feeData ? [{label: 'Fee data', value:
+ {[{label: 'Mean', value: feeData.mean}, {label: 'Median', value: feeData.median}, {label: 'Mode', value: feeData.mode}] + .map(({label, value}, key) => { + return +
+ {label} + {value.toFixed(4)} +
+
+ }) + }
- } + }] : []) ]}/>
gotoSingleBlockDetailsView(block.hash)}> diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index 5327326e93c..af2e803eb73 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -2,7 +2,7 @@ import {FC, useEffect, useRef, useState} from 'react'; import {useApi} from 'src/api/api'; import {Chart as ChartJS} from 'chart.js'; import {colorCodes} from 'src/utilities/constants'; -import {BitcoinBlockType} from 'src/utilities/models'; +import {BitcoinBlockType, FeeData} from 'src/utilities/models'; import styled, { useTheme } from 'styled-components'; import { getName } from 'src/utilities/helper-methods'; import Dropdown from './dropdown'; @@ -23,7 +23,7 @@ const ChartTileHeader = styled.span` font-weight: bolder; `; -const ChainHeader: FC<{ currency: string; network: string; blocks?: BitcoinBlockType[] }> = ({ currency, network, blocks }) => { +const ChainHeader: FC<{ currency: string; network: string; blocks?: Array> }> = ({ currency, network, blocks }) => { const theme = useTheme(); const priceDetails: { data: { @@ -63,14 +63,15 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: BitcoinBlock const [feeChangeSpan, setFeeChangeSpan] = useState(() => { return null; }); const [priceChangeSpan, setPriceChangeSpan] = useState(() => { return null; }); + const hasFees = blocks?.at(0)?.feeData !== undefined; useEffect(() => { - if (feeChartRef.current && blocks) { + if (feeChartRef.current && blocks && hasFees) { if (feeChartInstanceRef.current) { feeChartInstanceRef.current.destroy(); } const num = Number(feeSelectedRange.slice(0, feeSelectedRange.indexOf(' '))); - const fees = blocks.map((block: BitcoinBlockType) => block.feeData.median).reverse().slice(blocks.length - num); + const fees = blocks.map((block: BitcoinBlockType & Partial) => block.feeData?.median as number).reverse().slice(blocks.length - num); const dates = blocks.map((block: BitcoinBlockType) => new Date(block.time).toLocaleString('en-US', { year: '2-digit', @@ -207,7 +208,7 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: BitcoinBlock />
- + {getName(currency)} Exchange Rate
${price.toLocaleString()} @@ -218,17 +219,19 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: BitcoinBlock
- - {getName(currency)} Fee -
- {blocks?.at(0)?.feeData.median.toFixed(3)} sats/byte - -
- {feeChangeSpan} -
- -
-
+ { hasFees && + + {getName(currency)} Fee +
+ {blocks?.at(0)?.feeData?.median.toFixed(3)} sats/byte + +
+ {feeChangeSpan} +
+ +
+
+ }
diff --git a/packages/insight/src/utilities/models.ts b/packages/insight/src/utilities/models.ts index 7c1bbc33743..1394d174efb 100644 --- a/packages/insight/src/utilities/models.ts +++ b/packages/insight/src/utilities/models.ts @@ -116,11 +116,14 @@ export type BitcoinBlockType = BlocksType & { nonce: number, reward: number, version: number, - confirmations: number, + confirmations: number +}; + +export type FeeData = { feeData: { feeTotal: number; mean: number; median: number; mode: number; } -}; +} \ No newline at end of file From c562399f4a903b114e4630dd1dadeb5ca6bac356 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 4 Nov 2025 14:04:36 -0500 Subject: [PATCH 47/57] renamed block-sample -> block-list and removed blocks page --- packages/insight/src/Routing.tsx | 4 +- .../{block-sample.tsx => block-list.tsx} | 4 +- packages/insight/src/pages/blocks.tsx | 188 ------------------ packages/insight/src/pages/chain.tsx | 4 +- 4 files changed, 5 insertions(+), 195 deletions(-) rename packages/insight/src/components/{block-sample.tsx => block-list.tsx} (98%) delete mode 100644 packages/insight/src/pages/blocks.tsx diff --git a/packages/insight/src/Routing.tsx b/packages/insight/src/Routing.tsx index c0b337ac30b..2c88433241a 100644 --- a/packages/insight/src/Routing.tsx +++ b/packages/insight/src/Routing.tsx @@ -1,8 +1,7 @@ -import React, {lazy, Suspense} from 'react'; +import {lazy, Suspense} from 'react'; import {Navigate, Route, Routes} from 'react-router-dom'; import Home from './pages'; import Chain from './pages/chain'; -const Blocks = lazy(() => import('./pages/blocks')); const Block = lazy(() => import('./pages/block')); const TransactionHash = lazy(() => import('./pages/transaction')); const Address = lazy(() => import('./pages/address')); @@ -14,7 +13,6 @@ function Routing() { } /> } /> - } /> } /> } /> } /> diff --git a/packages/insight/src/components/block-sample.tsx b/packages/insight/src/components/block-list.tsx similarity index 98% rename from packages/insight/src/components/block-sample.tsx rename to packages/insight/src/components/block-list.tsx index 134823b9412..11feafbc776 100644 --- a/packages/insight/src/components/block-sample.tsx +++ b/packages/insight/src/components/block-list.tsx @@ -34,7 +34,7 @@ const getBlocksUrl = (currency: string, network: string) => { return `${getApiRoot(currency)}/${currency}/${network}/block?limit=200`; }; -const BlockSample: FC<{currency: string, network: string, blocks: Array>}> = ({currency, network, blocks}) => { +const BlockList: FC<{currency: string, network: string, blocks: Array>}> = ({currency, network, blocks}) => { const theme = useTheme(); const navigate = useNavigate(); const [expandedBlocks, setExpandedBlocks] = useState([]); @@ -182,4 +182,4 @@ const BlockSample: FC<{currency: string, network: string, blocks: Array (dark ? '#090909' : NeutralSlate)}; - font-size: 16px; - - @media screen and (max-width: ${size.tablet}) { - font-size: 14px; - line-height: 30px; - } -`; - -const BlockListTableRow = styled(motion.tr)` - text-align: center; - line-height: 45px; - - &:nth-child(odd) { - background-color: ${({theme: {dark}}) => (dark ? LightBlack : Slate30)}; - } - - &:nth-child(even) { - background-color: ${({theme: {dark}}) => (dark ? '#090909' : NeutralSlate)}; - } - - transition: transform 200ms ease, box-shadow 200ms ease; - font-size: 16px; - - @media screen and (max-width: ${size.tablet}) { - font-size: 14px; - line-height: 30px; - } -`; - -const TdLink = styled.td` - color: ${({theme: {colors}}) => colors.link}; -`; - -const getBlocksUrl = (currency: string, network: string) => { - return `${getApiRoot(currency)}/${currency}/${network}/block?limit=200`; -}; - -const listAnime = { - whileHover: { - cursor: 'pointer', - scale: 1.02, - transition: { - bounce: 0, - duration: 0.05, - ease: 'linear', - }, - }, -}; - -const Blocks: React.FC = () => { - let {currency, network} = useParams<{currency: string; network: string}>(); - const dispatch = useAppDispatch(); - const navigate = useNavigate(); - const [isLoading, setIsLoading] = useState(true); - const [blocksList, setBlocksList] = useState(); - const [error, setError] = useState(''); - const [hasMore, setHasMore] = useState(true); - - useEffect(() => { - if (!currency || !network) return; - nProgress.start(); - const _normalizeParams = normalizeParams(currency, network); - currency = _normalizeParams.currency; - network = _normalizeParams.network; - - dispatch(changeCurrency(currency)); - dispatch(changeNetwork(network)); - - Promise.all([fetcher(getBlocksUrl(currency, network)), sleep(500)]) - .then(([data]) => { - setBlocksList(data); - }) - .catch((e: any) => { - setError(e.message || 'Something went wrong. Please try again later.'); - }) - .finally(() => { - setIsLoading(false); - nProgress.done(); - }); - }, [currency, network]); - - const fetchMore = async (_blocksList: BlocksType[]) => { - if (!_blocksList.length || !currency || !network) return; - const since = _blocksList[_blocksList.length - 1].height; - try { - const newData: [BlocksType] = await fetcher( - `${getBlocksUrl(currency, network)}&since=${since}&paging=height&direction=-1`, - ); - if (newData?.length) { - setBlocksList(_blocksList.concat(newData)); - } else { - setHasMore(false); - } - } catch (e: any) { - setError(e.message || 'Something went wrong. Please try again later.'); - } - }; - - const gotoSingleBlockDetailsView = async (hash: string) => { - await navigate(`/${currency}/${network}/block/${hash}`); - }; - - return ( - <> - {!isLoading ? ( - <> - {error ? : null} - {blocksList?.length ? ( - - - Blocks - {currency && } - - - fetchMore(blocksList)} - hasMore={hasMore} - loader={} - dataLength={blocksList.length}> - - - - - - - - - - - {blocksList.map((block: BlocksType, index: number) => { - const {height, hash, transactionCount, time, size} = block; - return ( - gotoSingleBlockDetailsView(hash)}> - {height} - - - - - ); - })} - - - - - ) : null} - - ) : null} - - ); -}; - -export default Blocks; diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/chain.tsx index a2a1869c83e..aac4ef9c0e3 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/chain.tsx @@ -1,4 +1,4 @@ -import BlockSample from 'src/components/block-sample'; +import BlockList from 'src/components/block-list'; import React, {useEffect, useState} from 'react'; import ChainHeader from '../components/chain-header'; import {useParams} from 'react-router-dom'; @@ -49,7 +49,7 @@ const Chain: React.FC = () => { <> {error ? : null} - { blocksList && } + { blocksList && } ); } From bad27d59cd1cc992a6debee136b33874e0bfa70f Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 4 Nov 2025 14:37:15 -0500 Subject: [PATCH 48/57] renamed chain page to blocks page; renamed InfoCard -> info-card --- packages/insight/src/Routing.tsx | 4 ++-- packages/insight/src/components/block-list.tsx | 2 +- packages/insight/src/components/currency-tile.tsx | 2 +- .../insight/src/components/{InfoCard.tsx => info-card.tsx} | 0 packages/insight/src/pages/{chain.tsx => blocks.tsx} | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) rename packages/insight/src/components/{InfoCard.tsx => info-card.tsx} (100%) rename packages/insight/src/pages/{chain.tsx => blocks.tsx} (97%) diff --git a/packages/insight/src/Routing.tsx b/packages/insight/src/Routing.tsx index 2c88433241a..b8103c7520c 100644 --- a/packages/insight/src/Routing.tsx +++ b/packages/insight/src/Routing.tsx @@ -1,7 +1,7 @@ import {lazy, Suspense} from 'react'; import {Navigate, Route, Routes} from 'react-router-dom'; import Home from './pages'; -import Chain from './pages/chain'; +import Blocks from './pages/blocks'; const Block = lazy(() => import('./pages/block')); const TransactionHash = lazy(() => import('./pages/transaction')); const Address = lazy(() => import('./pages/address')); @@ -12,7 +12,7 @@ function Routing() { } /> - } /> + } /> } /> } /> } /> diff --git a/packages/insight/src/components/block-list.tsx b/packages/insight/src/components/block-list.tsx index 11feafbc776..ee863bf92df 100644 --- a/packages/insight/src/components/block-list.tsx +++ b/packages/insight/src/components/block-list.tsx @@ -7,7 +7,7 @@ import ArrowOutward from '../assets/images/arrow-outward.svg'; import ForwardArrow from '../assets/images/arrow-forward-blue.svg'; import ArrowDown from '../assets/images/arrow-down.svg'; import styled, { useTheme } from 'styled-components'; -import InfoCard from './InfoCard'; +import InfoCard from './info-card'; import InfiniteScroll from 'react-infinite-scroll-component'; import { fetcher } from 'src/api/api'; import InfiniteScrollLoadSpinner from './infinite-scroll-load-spinner'; diff --git a/packages/insight/src/components/currency-tile.tsx b/packages/insight/src/components/currency-tile.tsx index 782aeed5de1..31fe53bf543 100644 --- a/packages/insight/src/components/currency-tile.tsx +++ b/packages/insight/src/components/currency-tile.tsx @@ -198,7 +198,7 @@ const CurrencyTile: FC = ({currency}) => { const imgSrc = `https://bitpay.com/img/icon/currencies/${currency}.svg`; const gotoChain = async () => { - await navigate(`/${currency}/mainnet/chain`); + await navigate(`/${currency}/mainnet/blocks`); }; return ( diff --git a/packages/insight/src/components/InfoCard.tsx b/packages/insight/src/components/info-card.tsx similarity index 100% rename from packages/insight/src/components/InfoCard.tsx rename to packages/insight/src/components/info-card.tsx diff --git a/packages/insight/src/pages/chain.tsx b/packages/insight/src/pages/blocks.tsx similarity index 97% rename from packages/insight/src/pages/chain.tsx rename to packages/insight/src/pages/blocks.tsx index aac4ef9c0e3..215ce11c588 100644 --- a/packages/insight/src/pages/chain.tsx +++ b/packages/insight/src/pages/blocks.tsx @@ -10,7 +10,7 @@ import nProgress from 'nprogress'; import {BitcoinBlockType} from 'src/utilities/models'; import Info from 'src/components/info'; -const Chain: React.FC = () => { +const Blocks: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); const dispatch = useDispatch(); @@ -54,4 +54,4 @@ const Chain: React.FC = () => { ); } -export default Chain; \ No newline at end of file +export default Blocks; \ No newline at end of file From fb66c40938d44a2742f0f12658d5a50d17e3bd08 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Wed, 5 Nov 2025 12:34:10 -0500 Subject: [PATCH 49/57] refactored expanded data with a list of all data that can be showed and arrays for which rows should be on which chains --- .../insight/src/components/block-list.tsx | 89 +++++++++++-------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/packages/insight/src/components/block-list.tsx b/packages/insight/src/components/block-list.tsx index ee863bf92df..46a64a948fa 100644 --- a/packages/insight/src/components/block-list.tsx +++ b/packages/insight/src/components/block-list.tsx @@ -90,6 +90,58 @@ const BlockList: FC<{currency: string, network: string, blocks: Array, index: number) => { const feeData = block.feeData; const expanded = expandedBlocks.includes(block.height); + + const dataRowsDB = { + 'Previous block': {label: 'Previous block', value: block.height - 1}, + 'Bits': {label: 'Bits', value: block.bits}, + 'Version': {label: 'Version', value: block.version}, + 'Block reward': {label: 'Block reward', value: `${getConvertedValue(block.reward, currency).toFixed(3)} ${currency}`}, + 'Miner fees': {label: 'Miner fees', value: `${getConvertedValue(feeData?.feeTotal, currency).toFixed(5)} ${currency}`}, + 'Next block': {label: 'Next block', value: + <> + {block.height + 1} + gotoSingleBlockDetailsView(blocksList[index - 1].hash)} + alt='Next Block' + title={`Go to block ${block.height + 1}`} + /> + + }, + 'Nonce': {label: 'Nonce', value: block.nonce}, + 'Confirmations': {label: 'Confirmations', value: blocksList[0].height - block.height + 1}, + 'Difficulty': {label: 'Difficulty', value: getDifficultyFromBits(block.bits).toFixed(0)}, + 'Fee data': {label: 'Fee data', value: +
+ {[{label: 'Mean', value: feeData?.mean}, {label: 'Median', value: feeData?.median}, {label: 'Mode', value: feeData?.mode}] + .map(({label, value}, key) => { + return +
+ {label} + {value?.toFixed(4)} +
+
+ }) + } +
+ } + }; + + type IDataRowsDB = keyof typeof dataRowsDB; + let columnLeftExpandedDataKeys: IDataRowsDB[]; + let columnRightExpandedDataKeys: IDataRowsDB[]; + + if (currency === 'ETH') { + columnLeftExpandedDataKeys = ['Previous block', 'Block reward']; + columnRightExpandedDataKeys = ['Next block', 'Nonce', 'Confirmations']; + } else { + columnLeftExpandedDataKeys = ['Previous block', 'Bits', 'Version', 'Block reward', 'Miner fees']; + columnRightExpandedDataKeys = ['Next block', 'Nonce', 'Confirmations', 'Difficulty', 'Fee data']; + } + const columnLeftExpandedData : Array<{label: string, value: any}> = columnLeftExpandedDataKeys.map(key => dataRowsDB[key]); + const columnRightExpandedData : Array<{label: string, value: any}> = columnRightExpandedDataKeys.map(key => dataRowsDB[key]); + return ( @@ -126,41 +178,8 @@ const BlockList: FC<{currency: string, network: string, blocks: Array Summary
- - - {block.height + 1} - gotoSingleBlockDetailsView(blocksList[index - 1].hash)} - alt='Next Block' - title={`Go to block ${block.height + 1}`} - /> - }, - {label: 'Nonce', value: block.nonce}, - {label: 'Confirmations', value: blocksList[0].height - block.height + 1}, - ...(block.bits ? [{label: 'Difficulty', value: getDifficultyFromBits(block.bits).toFixed(0)}] : []), - ...(feeData ? [{label: 'Fee data', value:
- {[{label: 'Mean', value: feeData.mean}, {label: 'Median', value: feeData.median}, {label: 'Mode', value: feeData.mode}] - .map(({label, value}, key) => { - return -
- {label} - {value.toFixed(4)} -
-
- }) - } -
- }] : []) - ]}/> + +
gotoSingleBlockDetailsView(block.hash)}> View transactions From 15dffb4d4a0f80a3d81e308c5f05c6bbb4126b9d Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 11 Nov 2025 12:49:08 -0500 Subject: [PATCH 50/57] Michal Stachnik's review changes: fee and price change components and block context provider --- packages/insight/src/App.tsx | 21 +++-- packages/insight/src/Routing.tsx | 2 +- .../insight/src/components/block-list.tsx | 35 ++++---- .../insight/src/components/chain-header.tsx | 86 ++++++++++++------- .../insight/src/components/currency-tile.tsx | 4 +- packages/insight/src/pages/blocks.tsx | 40 +++++++-- .../insight/src/utilities/helper-methods.ts | 6 +- packages/insight/src/utilities/models.ts | 15 ++-- 8 files changed, 130 insertions(+), 79 deletions(-) diff --git a/packages/insight/src/App.tsx b/packages/insight/src/App.tsx index 8388813f2b5..df4d14fcf80 100644 --- a/packages/insight/src/App.tsx +++ b/packages/insight/src/App.tsx @@ -9,6 +9,7 @@ import {GlobalStyles} from './assets/styles/global'; import {useAppSelector} from './utilities/hooks'; import 'nprogress/nprogress.css'; import nProgress from 'nprogress'; +import { BlocksProvider } from './pages/blocks'; function App() { const theme = useAppSelector(({APP}) => APP.theme); @@ -20,15 +21,17 @@ function App() { return ( - - - - - - + + + + + + + + ); } diff --git a/packages/insight/src/Routing.tsx b/packages/insight/src/Routing.tsx index b8103c7520c..9fddf5cfdf6 100644 --- a/packages/insight/src/Routing.tsx +++ b/packages/insight/src/Routing.tsx @@ -1,7 +1,7 @@ import {lazy, Suspense} from 'react'; import {Navigate, Route, Routes} from 'react-router-dom'; import Home from './pages'; -import Blocks from './pages/blocks'; +const Blocks = lazy(() => import('./pages/blocks')); const Block = lazy(() => import('./pages/block')); const TransactionHash = lazy(() => import('./pages/transaction')); const Address = lazy(() => import('./pages/address')); diff --git a/packages/insight/src/components/block-list.tsx b/packages/insight/src/components/block-list.tsx index 46a64a948fa..0e1e756c963 100644 --- a/packages/insight/src/components/block-list.tsx +++ b/packages/insight/src/components/block-list.tsx @@ -1,18 +1,19 @@ import React, {FC, useState} from 'react'; import {getApiRoot, getConvertedValue, getDifficultyFromBits, getFormattedDate} from 'src/utilities/helper-methods'; -import {BitcoinBlockType, FeeData} from 'src/utilities/models'; +import {BitcoinBlockType} from 'src/utilities/models'; import Cube from '../assets/images/cube.svg'; import Arrow from '../assets/images/arrow-thin.svg'; import ArrowOutward from '../assets/images/arrow-outward.svg'; import ForwardArrow from '../assets/images/arrow-forward-blue.svg'; import ArrowDown from '../assets/images/arrow-down.svg'; -import styled, { useTheme } from 'styled-components'; +import styled, {useTheme} from 'styled-components'; import InfoCard from './info-card'; import InfiniteScroll from 'react-infinite-scroll-component'; -import { fetcher } from 'src/api/api'; +import {fetcher} from 'src/api/api'; import InfiniteScrollLoadSpinner from './infinite-scroll-load-spinner'; import Info from './info'; -import { useNavigate } from 'react-router-dom'; +import {useNavigate} from 'react-router-dom'; +import {useBlocks} from 'src/pages/blocks'; const BlockListTableRow = styled.tr` text-align: center; @@ -34,11 +35,13 @@ const getBlocksUrl = (currency: string, network: string) => { return `${getApiRoot(currency)}/${currency}/${network}/block?limit=200`; }; -const BlockList: FC<{currency: string, network: string, blocks: Array>}> = ({currency, network, blocks}) => { +const BlockList: FC<{currency: string, network: string}> = ({currency, network}) => { const theme = useTheme(); + const { blocks, setBlocks } = useBlocks(); + if (!blocks) + return null; const navigate = useNavigate(); const [expandedBlocks, setExpandedBlocks] = useState([]); - const [blocksList, setBlocksList] = useState(blocks); const [error, setError] = useState(''); const [hasMore, setHasMore] = useState(true); const hasFees = blocks.every(block => block.feeData); @@ -52,7 +55,7 @@ const BlockList: FC<{currency: string, network: string, blocks: Array { - await navigate(`/${currency}/${network}/block/${hash}`); + const gotoSingleBlockDetailsView = (hash: string) => { + navigate(`/${currency}/${network}/block/${hash}`); }; - if (!blocksList?.length) return null; + if (!blocks?.length) return null; return ( <> - {error ? : null} + {error ? : null} fetchMore(blocksList)} + next={() => fetchMore(blocks)} hasMore={hasMore} loader={} - dataLength={blocksList.length}> + dataLength={blocks.length}>
HeightTimestampTransactionsSizeFee RateHeightTimestampTransactionsSizeFee Rate
{getFormattedDate(block.time)} {block.transactionCount} {block.size}{feeData.median.toFixed(4)}{feeData.median.toFixed(4)}
HeightTimestampTransactionsSize
{getFormattedDate(time)}{transactionCount}{size}
@@ -87,7 +90,7 @@ const BlockList: FC<{currency: string, network: string, blocks: Array { - blocksList.map((block: BitcoinBlockType & Partial, index: number) => { + blocks.map((block: BitcoinBlockType, index: number) => { const feeData = block.feeData; const expanded = expandedBlocks.includes(block.height); @@ -103,14 +106,14 @@ const BlockList: FC<{currency: string, network: string, blocks: Array gotoSingleBlockDetailsView(blocksList[index - 1].hash)} + onClick={() => gotoSingleBlockDetailsView(blocks[index - 1].hash)} alt='Next Block' title={`Go to block ${block.height + 1}`} /> }, 'Nonce': {label: 'Nonce', value: block.nonce}, - 'Confirmations': {label: 'Confirmations', value: blocksList[0].height - block.height + 1}, + 'Confirmations': {label: 'Confirmations', value: blocks[0].height - block.height + 1}, 'Difficulty': {label: 'Difficulty', value: getDifficultyFromBits(block.bits).toFixed(0)}, 'Fee data': {label: 'Fee data', value:
diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index af2e803eb73..448fccd2390 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -2,10 +2,11 @@ import {FC, useEffect, useRef, useState} from 'react'; import {useApi} from 'src/api/api'; import {Chart as ChartJS} from 'chart.js'; import {colorCodes} from 'src/utilities/constants'; -import {BitcoinBlockType, FeeData} from 'src/utilities/models'; -import styled, { useTheme } from 'styled-components'; -import { getName } from 'src/utilities/helper-methods'; +import {BitcoinBlockType} from 'src/utilities/models'; +import styled, {useTheme} from 'styled-components'; +import {getName} from 'src/utilities/helper-methods'; import Dropdown from './dropdown'; +import {useBlocks} from 'src/pages/blocks'; const ChartTile = styled.div` height: 400px; @@ -23,8 +24,47 @@ const ChartTileHeader = styled.span` font-weight: bolder; `; -const ChainHeader: FC<{ currency: string; network: string; blocks?: Array> }> = ({ currency, network, blocks }) => { +interface ChangeData { + change: number; + percentChange: number; + range: number; +} + +const FeeChangeData: FC<{ data?: ChangeData }> = ({ data }) => { + if (!data) + return null; + const { change, percentChange, range } = data; + return ( + + {change.toFixed(2)} sats/byte ({percentChange.toFixed(2)}%) + Last {range} + + ); +} + +const PriceChangeData: FC<{ data?: ChangeData }> = ({ data }) => { + if (!data) + return null; + const { change, percentChange, range } = data; + + let color = 'gray'; + if (change > 0) { + color = 'green'; + } else if (change < 0) { + color = 'red'; + } + + return ( + + ${change.toFixed(2)} ({percentChange.toFixed(2)}%) + Last {range} + + ); +} + +const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, network }) => { const theme = useTheme(); + const { blocks } = useBlocks(); const priceDetails: { data: { code: string, @@ -61,8 +101,8 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: Array { return null; }); - const [priceChangeSpan, setPriceChangeSpan] = useState(() => { return null; }); + const [priceChangeData, setPriceChangeData] = useState(); + const [feeChangeData, setFeeChangeData] = useState(); const hasFees = blocks?.at(0)?.feeData !== undefined; useEffect(() => { @@ -70,8 +110,8 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: Array) => block.feeData?.median as number).reverse().slice(blocks.length - num); + const numBlocks = Number(feeSelectedRange.slice(0, feeSelectedRange.indexOf(' '))); + const fees = blocks.map((block: BitcoinBlockType) => block.feeData?.median as number).reverse().slice(blocks.length - numBlocks); const dates = blocks.map((block: BitcoinBlockType) => new Date(block.time).toLocaleString('en-US', { year: '2-digit', @@ -80,7 +120,7 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: Array { - return - {feeChange.toFixed(2)} sats/byte ({percentFeeChange.toFixed(2)}%) - Last {feeSelectedRange} - - }); + + setFeeChangeData({change: feeChange, percentChange: percentFeeChange, range: numBlocks}); } return () => { @@ -180,19 +214,7 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: Array 0) { - color = 'green'; - } else if (priceChange < 0) { - color = 'red'; - } - - setPriceChangeSpan(() => { - return - ${priceChange.toFixed(2)} ({percentPriceChange.toFixed(2)}%) - Last {priceSelectedRange} - - }); + setPriceChangeData({change: priceChange, percentChange: percentPriceChange, range: hours}); return () => { priceChartInstanceRef.current?.destroy(); }; @@ -214,7 +236,7 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: Array${price.toLocaleString()}
- {priceChangeSpan} +
@@ -226,7 +248,7 @@ const ChainHeader: FC<{ currency: string; network: string; blocks?: Array{blocks?.at(0)?.feeData?.median.toFixed(3)} sats/byte - {feeChangeSpan} +
diff --git a/packages/insight/src/components/currency-tile.tsx b/packages/insight/src/components/currency-tile.tsx index 31fe53bf543..631dba11670 100644 --- a/packages/insight/src/components/currency-tile.tsx +++ b/packages/insight/src/components/currency-tile.tsx @@ -197,8 +197,8 @@ const CurrencyTile: FC = ({currency}) => { const {height, time, transactionCount, size} = data[0]; const imgSrc = `https://bitpay.com/img/icon/currencies/${currency}.svg`; - const gotoChain = async () => { - await navigate(`/${currency}/mainnet/blocks`); + const gotoChain = () => { + navigate(`/${currency}/mainnet/blocks`); }; return ( diff --git a/packages/insight/src/pages/blocks.tsx b/packages/insight/src/pages/blocks.tsx index 215ce11c588..49bd098669e 100644 --- a/packages/insight/src/pages/blocks.tsx +++ b/packages/insight/src/pages/blocks.tsx @@ -1,5 +1,5 @@ import BlockList from 'src/components/block-list'; -import React, {useEffect, useState} from 'react'; +import React, {createContext, useContext, useEffect, useState} from 'react'; import ChainHeader from '../components/chain-header'; import {useParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; @@ -10,26 +10,48 @@ import nProgress from 'nprogress'; import {BitcoinBlockType} from 'src/utilities/models'; import Info from 'src/components/info'; +type BlocksContextType = { + blocks: BitcoinBlockType[] | undefined; + setBlocks: React.Dispatch>; +}; + +const BlocksContext = createContext(undefined); + +export const BlocksProvider: React.FC<{children: React.ReactNode}> = ({ children }) => { + const [blocks, setBlocks] = useState(); + return ( + + {children} + + ); +}; + +export const useBlocks = () => { + const ctx = useContext(BlocksContext); + if (!ctx) throw new Error('useBlocks must be used within a BlocksProvider'); + return ctx; +}; + const Blocks: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); const dispatch = useDispatch(); - const [blocksList, setBlocksList] = useState(); + const { blocks, setBlocks } = useBlocks(); const [error, setError] = useState(''); useEffect(() => { nProgress.start(); if (!currency || !network) return; - Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=128`)]) + Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=64`)]) .then(([data]) => { - setBlocksList(data); - }) - .finally(() => { - nProgress.done(); + setBlocks(data); }) .catch((e: any) => { setError(e.message || 'Something went wrong. Please try again later.'); + }) + .finally(() => { + nProgress.done(); }); }, []); @@ -48,8 +70,8 @@ const Blocks: React.FC = () => { return ( <> {error ? : null} - - { blocksList && } + + { blocks && } ); } diff --git a/packages/insight/src/utilities/helper-methods.ts b/packages/insight/src/utilities/helper-methods.ts index 085926cbb7e..8c26f8d7219 100644 --- a/packages/insight/src/utilities/helper-methods.ts +++ b/packages/insight/src/utilities/helper-methods.ts @@ -172,7 +172,6 @@ export const normalizeParams = ( export const getLib = (currency: string) => { switch (currency.toUpperCase()) { case 'BTC': - default: return BitcoreLib; case 'BCH': return BitcoreLibCash; @@ -180,13 +179,14 @@ export const getLib = (currency: string) => { return BitcoreLibDoge; case 'LTC': return BitcoreLibLtc; + default: + return BitcoreLib; } }; export const getName = (currency: string) => { switch (currency.toUpperCase()) { case 'BTC': - default: return 'Bitcoin'; case 'BCH': return 'Bitcoin Cash'; @@ -196,6 +196,8 @@ export const getName = (currency: string) => { return 'Litecoin'; case 'ETH': return 'Ethereum'; + default: + return 'Bitcoin'; } } diff --git a/packages/insight/src/utilities/models.ts b/packages/insight/src/utilities/models.ts index 1394d174efb..cddc781fffc 100644 --- a/packages/insight/src/utilities/models.ts +++ b/packages/insight/src/utilities/models.ts @@ -116,14 +116,13 @@ export type BitcoinBlockType = BlocksType & { nonce: number, reward: number, version: number, - confirmations: number + confirmations: number, + feeData: FeeData; }; export type FeeData = { - feeData: { - feeTotal: number; - mean: number; - median: number; - mode: number; - } -} \ No newline at end of file + feeTotal: number; + mean: number; + median: number; + mode: number; +} From edc22daadd561afcd194d75d5fba8bb05ce15afd Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 11 Nov 2025 22:26:17 -0500 Subject: [PATCH 51/57] moved change indication components to their own files --- .../insight/src/components/chain-header.tsx | 43 ++---------------- .../insight/src/components/change-span.tsx | 44 +++++++++++++++++++ 2 files changed, 47 insertions(+), 40 deletions(-) create mode 100644 packages/insight/src/components/change-span.tsx diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index 448fccd2390..01545d7e05a 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -7,6 +7,7 @@ import styled, {useTheme} from 'styled-components'; import {getName} from 'src/utilities/helper-methods'; import Dropdown from './dropdown'; import {useBlocks} from 'src/pages/blocks'; +import { ChangeData, FeeChangeSpan, PriceChangeSpan } from './change-span'; const ChartTile = styled.div` height: 400px; @@ -24,44 +25,6 @@ const ChartTileHeader = styled.span` font-weight: bolder; `; -interface ChangeData { - change: number; - percentChange: number; - range: number; -} - -const FeeChangeData: FC<{ data?: ChangeData }> = ({ data }) => { - if (!data) - return null; - const { change, percentChange, range } = data; - return ( - - {change.toFixed(2)} sats/byte ({percentChange.toFixed(2)}%) - Last {range} - - ); -} - -const PriceChangeData: FC<{ data?: ChangeData }> = ({ data }) => { - if (!data) - return null; - const { change, percentChange, range } = data; - - let color = 'gray'; - if (change > 0) { - color = 'green'; - } else if (change < 0) { - color = 'red'; - } - - return ( - - ${change.toFixed(2)} ({percentChange.toFixed(2)}%) - Last {range} - - ); -} - const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, network }) => { const theme = useTheme(); const { blocks } = useBlocks(); @@ -236,7 +199,7 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw ${price.toLocaleString()} - +
@@ -248,7 +211,7 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw {blocks?.at(0)?.feeData?.median.toFixed(3)} sats/byte - +
diff --git a/packages/insight/src/components/change-span.tsx b/packages/insight/src/components/change-span.tsx new file mode 100644 index 00000000000..d2624148f5d --- /dev/null +++ b/packages/insight/src/components/change-span.tsx @@ -0,0 +1,44 @@ +import {FC} from 'react'; + +export interface ChangeData { + change: number; + percentChange: number; + range: number; +} + +export const FeeChangeSpan: FC<{ data?: ChangeData }> = ({ data }) => { + if (!data) + return null; + const { change, percentChange, range } = data; + return ( + + + {change.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} sats/byte + ({percentChange.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%) + Last {range} Days + + ); +} + +export const PriceChangeSpan: FC<{ data?: ChangeData }> = ({ data }) => { + if (!data) + return null; + const { change, percentChange, range } = data; + + let color = 'gray'; + if (change > 0) { + color = 'green'; + } else if (change < 0) { + color = 'red'; + } + + return ( + + + ${change.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}{' '} + ({percentChange.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%) + + Last {range} Hours + + ); +} \ No newline at end of file From 215f0b516d060fbdc132c187cbb1bdf93b86e251 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Thu, 13 Nov 2025 12:32:27 -0500 Subject: [PATCH 52/57] increased initial blocks --- packages/insight/src/pages/blocks.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/insight/src/pages/blocks.tsx b/packages/insight/src/pages/blocks.tsx index 49bd098669e..d5a27ecade4 100644 --- a/packages/insight/src/pages/blocks.tsx +++ b/packages/insight/src/pages/blocks.tsx @@ -43,7 +43,7 @@ const Blocks: React.FC = () => { nProgress.start(); if (!currency || !network) return; - Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=64`)]) + Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=200`)]) .then(([data]) => { setBlocks(data); }) From 9dce1bace7cae6c7c9c16e2d885ad6f90a284b4b Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Mon, 17 Nov 2025 11:56:12 -0500 Subject: [PATCH 53/57] Michal Stachnik review changes: moved useBlocks and BlocksProvider to its own file --- packages/insight/src/App.tsx | 2 +- .../insight/src/components/block-list.tsx | 8 +++--- .../insight/src/components/chain-header.tsx | 4 +-- .../insight/src/components/change-span.tsx | 2 +- packages/insight/src/contexts.tsx | 24 +++++++++++++++++ packages/insight/src/pages/blocks.tsx | 26 ++----------------- 6 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 packages/insight/src/contexts.tsx diff --git a/packages/insight/src/App.tsx b/packages/insight/src/App.tsx index df4d14fcf80..6350b7bd14c 100644 --- a/packages/insight/src/App.tsx +++ b/packages/insight/src/App.tsx @@ -9,7 +9,7 @@ import {GlobalStyles} from './assets/styles/global'; import {useAppSelector} from './utilities/hooks'; import 'nprogress/nprogress.css'; import nProgress from 'nprogress'; -import { BlocksProvider } from './pages/blocks'; +import {BlocksProvider} from './contexts'; function App() { const theme = useAppSelector(({APP}) => APP.theme); diff --git a/packages/insight/src/components/block-list.tsx b/packages/insight/src/components/block-list.tsx index 0e1e756c963..d18ed3bbfa4 100644 --- a/packages/insight/src/components/block-list.tsx +++ b/packages/insight/src/components/block-list.tsx @@ -13,7 +13,7 @@ import {fetcher} from 'src/api/api'; import InfiniteScrollLoadSpinner from './infinite-scroll-load-spinner'; import Info from './info'; import {useNavigate} from 'react-router-dom'; -import {useBlocks} from 'src/pages/blocks'; +import {useBlocks} from 'src/contexts'; const BlockListTableRow = styled.tr` text-align: center; @@ -38,8 +38,7 @@ const getBlocksUrl = (currency: string, network: string) => { const BlockList: FC<{currency: string, network: string}> = ({currency, network}) => { const theme = useTheme(); const { blocks, setBlocks } = useBlocks(); - if (!blocks) - return null; + if (!blocks?.length) return null; const navigate = useNavigate(); const [expandedBlocks, setExpandedBlocks] = useState([]); const [error, setError] = useState(''); @@ -68,7 +67,6 @@ const BlockList: FC<{currency: string, network: string}> = ({currency, network}) navigate(`/${currency}/${network}/block/${hash}`); }; - if (!blocks?.length) return null; return ( <> {error ? : null} @@ -147,7 +145,7 @@ const BlockList: FC<{currency: string, network: string}> = ({currency, network}) return ( - +
= ({ currency, netw {getName(currency)} Fee
- {blocks?.at(0)?.feeData?.median.toFixed(3)} sats/byte + {blocks?.at(0)?.feeData?.median.toLocaleString()} sats/byte
diff --git a/packages/insight/src/components/change-span.tsx b/packages/insight/src/components/change-span.tsx index d2624148f5d..4f2888fa292 100644 --- a/packages/insight/src/components/change-span.tsx +++ b/packages/insight/src/components/change-span.tsx @@ -15,7 +15,7 @@ export const FeeChangeSpan: FC<{ data?: ChangeData }> = ({ data }) => { {change.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} sats/byte ({percentChange.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%) - Last {range} Days + Last {range} Blocks
); } diff --git a/packages/insight/src/contexts.tsx b/packages/insight/src/contexts.tsx new file mode 100644 index 00000000000..3f8a5ce6f82 --- /dev/null +++ b/packages/insight/src/contexts.tsx @@ -0,0 +1,24 @@ +import React, { createContext, useContext, useState } from 'react'; +import { BitcoinBlockType } from './utilities/models'; + +type BlocksContextType = { + blocks: BitcoinBlockType[] | undefined; + setBlocks: React.Dispatch>; +}; + +const BlocksContext = createContext(undefined); + +export const BlocksProvider: React.FC<{children: React.ReactNode}> = ({ children }) => { + const [blocks, setBlocks] = useState(); + return ( + + {children} + + ); +}; + +export const useBlocks = () => { + const ctx = useContext(BlocksContext); + if (!ctx) throw new Error('useBlocks must be used within a BlocksProvider'); + return ctx; +}; \ No newline at end of file diff --git a/packages/insight/src/pages/blocks.tsx b/packages/insight/src/pages/blocks.tsx index d5a27ecade4..9ba09d99c70 100644 --- a/packages/insight/src/pages/blocks.tsx +++ b/packages/insight/src/pages/blocks.tsx @@ -1,5 +1,5 @@ import BlockList from 'src/components/block-list'; -import React, {createContext, useContext, useEffect, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import ChainHeader from '../components/chain-header'; import {useParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; @@ -7,30 +7,8 @@ import {changeCurrency, changeNetwork} from 'src/store/app.actions'; import {getApiRoot, normalizeParams} from 'src/utilities/helper-methods'; import {fetcher} from 'src/api/api'; import nProgress from 'nprogress'; -import {BitcoinBlockType} from 'src/utilities/models'; import Info from 'src/components/info'; - -type BlocksContextType = { - blocks: BitcoinBlockType[] | undefined; - setBlocks: React.Dispatch>; -}; - -const BlocksContext = createContext(undefined); - -export const BlocksProvider: React.FC<{children: React.ReactNode}> = ({ children }) => { - const [blocks, setBlocks] = useState(); - return ( - - {children} - - ); -}; - -export const useBlocks = () => { - const ctx = useContext(BlocksContext); - if (!ctx) throw new Error('useBlocks must be used within a BlocksProvider'); - return ctx; -}; +import {useBlocks} from 'src/contexts'; const Blocks: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); From f4f117c4c881e8484692338e4c7050ae54d82014 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Thu, 20 Nov 2025 09:35:20 -0500 Subject: [PATCH 54/57] moved data computation to fee and price change spans --- .../insight/src/components/chain-header.tsx | 17 ++-------- .../insight/src/components/change-span.tsx | 31 ++++++++++--------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index 7b5540eca46..02df2e9a226 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -7,7 +7,7 @@ import styled, {useTheme} from 'styled-components'; import {getName} from 'src/utilities/helper-methods'; import Dropdown from './dropdown'; import {useBlocks} from 'src/contexts'; -import { ChangeData, FeeChangeSpan, PriceChangeSpan } from './change-span'; +import {FeeChangeSpan, PriceChangeSpan} from './change-span'; const ChartTile = styled.div` height: 400px; @@ -64,8 +64,6 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw const [feeSelectedRange, setFeesSelectedRange] = useState('32 Blocks'); const [priceSelectedRange, setPriceSelectedRange] = useState('24 Hours'); - const [priceChangeData, setPriceChangeData] = useState(); - const [feeChangeData, setFeeChangeData] = useState(); const hasFees = blocks?.at(0)?.feeData !== undefined; useEffect(() => { @@ -117,11 +115,6 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw data: chartData, options }); - - const feeChange = fees[fees.length - 1] - fees[0]; - const percentFeeChange = feeChange / fees[0] * 100; - - setFeeChangeData({change: feeChange, percentChange: percentFeeChange, range: numBlocks}); } return () => { @@ -174,10 +167,6 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw }); } - const priceChange = price - usedPrices[0]; - const percentPriceChange = priceChange / usedPrices[0] * 100; - - setPriceChangeData({change: priceChange, percentChange: percentPriceChange, range: hours}); return () => { priceChartInstanceRef.current?.destroy(); }; @@ -199,7 +188,7 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw ${price.toLocaleString()} - +
@@ -211,7 +200,7 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw {blocks?.at(0)?.feeData?.median.toLocaleString()} sats/byte - +
diff --git a/packages/insight/src/components/change-span.tsx b/packages/insight/src/components/change-span.tsx index 4f2888fa292..93760c638b6 100644 --- a/packages/insight/src/components/change-span.tsx +++ b/packages/insight/src/components/change-span.tsx @@ -1,29 +1,30 @@ import {FC} from 'react'; +import {useBlocks} from 'src/contexts'; -export interface ChangeData { - change: number; - percentChange: number; - range: number; -} - -export const FeeChangeSpan: FC<{ data?: ChangeData }> = ({ data }) => { - if (!data) +export const FeeChangeSpan: FC<{ range: string }> = ({ range }) => { + const { blocks } = useBlocks(); + if (!blocks) return null; - const { change, percentChange, range } = data; + + const numBlocks = Number(range.slice(0, range.indexOf(' '))); + const fees = blocks?.map(block => block.feeData.median as number); + const change = fees[0] - fees[numBlocks]; + const percentChange = change / fees[numBlocks] * 100; + return ( {change.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} sats/byte ({percentChange.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%) - Last {range} Blocks + Last {numBlocks} Blocks ); } -export const PriceChangeSpan: FC<{ data?: ChangeData }> = ({ data }) => { - if (!data) - return null; - const { change, percentChange, range } = data; +export const PriceChangeSpan: FC<{ prices: number[], lastPrice: number, range: string }> = ({ prices, lastPrice, range }) => { + const hours = Number(range.slice(0, range.indexOf(' '))); + const change = lastPrice - prices[prices.length - hours]; + const percentChange = change / prices[prices.length - hours] * 100; let color = 'gray'; if (change > 0) { @@ -38,7 +39,7 @@ export const PriceChangeSpan: FC<{ data?: ChangeData }> = ({ data }) => { ${change.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}{' '} ({percentChange.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%) - Last {range} Hours + Last {hours} Hours ); } \ No newline at end of file From 6d111af3572342373703f87d81113849f5f2ee00 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Thu, 11 Dec 2025 13:46:45 -0500 Subject: [PATCH 55/57] fixed reload break error --- .../insight/src/components/chain-header.tsx | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index 02df2e9a226..e3b84677103 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -1,5 +1,5 @@ import {FC, useEffect, useRef, useState} from 'react'; -import {useApi} from 'src/api/api'; +import {fetcher, useApi} from 'src/api/api'; import {Chart as ChartJS} from 'chart.js'; import {colorCodes} from 'src/utilities/constants'; import {BitcoinBlockType} from 'src/utilities/models'; @@ -25,16 +25,18 @@ const ChartTileHeader = styled.span` font-weight: bolder; `; +interface PriceDetails { + data: { + code: string, + name: string, + rate: number + } +} + const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, network }) => { const theme = useTheme(); const { blocks } = useBlocks(); - const priceDetails: { - data: { - code: string, - name: string, - rate: number - } - } = useApi(`https://bitpay.com/rates/${currency}/usd`).data; + const [price, setPrice] = useState(0); const priceDisplay: { data: Array<{ @@ -49,7 +51,6 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, ).data; - const price = network === 'mainnet' ? priceDetails?.data?.rate : 0; const feeChartRef = useRef(null); const feeChartInstanceRef = useRef(null); @@ -125,6 +126,15 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw useEffect(() => { const hours = Number(priceSelectedRange.slice(0, priceSelectedRange.indexOf(' '))) const usedPrices = priceList.slice(priceList.length - hours); + if (network !== 'mainnet') { + setPrice(0); + } else { + fetcher(`https://bitpay.com/rates/${currency}/usd`) + .then(({data}: PriceDetails) => { + setPrice(data.rate); + }); + } + const priceChartData = { labels: usedPrices, datasets: [ From eb02cedb0c458b1e35660065141dba79652cc956 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 16 Dec 2025 13:24:42 -0500 Subject: [PATCH 56/57] handle price fetching errors on chain page --- .../insight/src/components/chain-header.tsx | 71 ++++++++++++------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index e3b84677103..cb8b0357094 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -1,5 +1,5 @@ import {FC, useEffect, useRef, useState} from 'react'; -import {fetcher, useApi} from 'src/api/api'; +import {fetcher} from 'src/api/api'; import {Chart as ChartJS} from 'chart.js'; import {colorCodes} from 'src/utilities/constants'; import {BitcoinBlockType} from 'src/utilities/models'; @@ -8,6 +8,7 @@ import {getName} from 'src/utilities/helper-methods'; import Dropdown from './dropdown'; import {useBlocks} from 'src/contexts'; import {FeeChangeSpan, PriceChangeSpan} from './change-span'; +import Info from './info'; const ChartTile = styled.div` height: 400px; @@ -33,31 +34,29 @@ interface PriceDetails { } } +interface PriceDisplay { + data: Array<{ + prices: Array<{price: number, time: string}>, + currencyPair: string, + currencies: Array, + priceDisplay: Array, + percentChange: string, + priceDisplayPercentChange: string + }> +} + const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, network }) => { const theme = useTheme(); const { blocks } = useBlocks(); const [price, setPrice] = useState(0); - - const priceDisplay: { - data: Array<{ - prices: Array<{price: number, time: string}>, - currencyPair: string, - currencies: Array, - priceDisplay: Array, - percentChange: string, - priceDisplayPercentChange: string - }> - } = useApi( - `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`, - ).data; - + const [priceList, setPriceList] = useState([0]); + const [error, setError] = useState(''); const feeChartRef = useRef(null); const feeChartInstanceRef = useRef(null); const priceChartRef = useRef(null); const priceChartInstanceRef = useRef(null); - const priceList = (priceDisplay?.data?.[0]?.priceDisplay || []); const feeRanges = ['128 Blocks', '32 Blocks', '16 Blocks', '8 Blocks']; const priceRanges = ['24 Hours', '12 Hours', '6 Hours', '3 Hours']; @@ -129,9 +128,13 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw if (network !== 'mainnet') { setPrice(0); } else { - fetcher(`https://bitpay.com/rates/${currency}/usd`) + const url = `https://bitpay.com/rates/${currency}/usd`; + fetcher(url) .then(({data}: PriceDetails) => { setPrice(data.rate); + }) + .catch(() => { + setError('Error fetching price. Please try again later.'); }); } @@ -182,6 +185,17 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw }; }, [priceList, price, priceSelectedRange, currency]); + useEffect(() => { + const url = `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`; + fetcher(url) + .then((priceDisplay: PriceDisplay) => { + setPriceList(priceDisplay.data[0].priceDisplay); + }) + .catch(() => { + setError('Error fetching price graph data. Please try again later.'); + }); + }, [currency]); + return (
Blocks @@ -192,16 +206,21 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw />
+ - {getName(currency)} Exchange Rate -
- ${price.toLocaleString()} - -
- -
- -
+ { error ? : + <> + {getName(currency)} Exchange Rate +
+ ${price.toLocaleString()} + +
+ +
+ +
+ + }
{ hasFees && From 708fd9a9f3fde630f91b8600cab59e139d64e1d4 Mon Sep 17 00:00:00 2001 From: Micah Maphet Date: Tue, 16 Dec 2025 16:38:37 -0500 Subject: [PATCH 57/57] display graphs in a column for mobile, reorganized useEffect hooks in ChainHeader, and removed unused helper-methods --- .../insight/src/components/chain-header.tsx | 115 ++++++++++-------- packages/insight/src/components/dropdown.tsx | 4 +- packages/insight/src/components/info-card.tsx | 2 +- packages/insight/src/pages/blocks.tsx | 24 ++-- .../insight/src/utilities/helper-methods.ts | 40 ------ 5 files changed, 79 insertions(+), 106 deletions(-) diff --git a/packages/insight/src/components/chain-header.tsx b/packages/insight/src/components/chain-header.tsx index cb8b0357094..55cb7ddeaa7 100644 --- a/packages/insight/src/components/chain-header.tsx +++ b/packages/insight/src/components/chain-header.tsx @@ -1,24 +1,45 @@ import {FC, useEffect, useRef, useState} from 'react'; import {fetcher} from 'src/api/api'; import {Chart as ChartJS} from 'chart.js'; -import {colorCodes} from 'src/utilities/constants'; +import {colorCodes, size} from 'src/utilities/constants'; import {BitcoinBlockType} from 'src/utilities/models'; -import styled, {useTheme} from 'styled-components'; +import styled from 'styled-components'; import {getName} from 'src/utilities/helper-methods'; import Dropdown from './dropdown'; import {useBlocks} from 'src/contexts'; import {FeeChangeSpan, PriceChangeSpan} from './change-span'; import Info from './info'; -const ChartTile = styled.div` +const ChartTile = styled.div<{ fullWidth?: boolean }>` height: 400px; - width: 50%; + width: ${({fullWidth}) => fullWidth ? '100%' : '50%'}; background-color: ${({theme: {dark}}) => dark ? '#222' : '#fff'}; border-radius: 10px; padding: 1.5rem; margin: 1rem; display: flex; flex-direction: column; + + @media screen and (max-width: ${size.mobileL}) { + width: 100%; + margin: 0.5rem; + } +`; + +const ChartContainer = styled.div` + display: flex; + flex-direction: row; + width: 100%; + align-items: center; + padding: 1rem; + background-color: ${({theme: {dark}}) => dark ? '#111' : '#f6f7f9'}; + height: fit-content; + margin-bottom: 2rem; + + @media screen and (max-width: ${size.mobileL}) { + flex-direction: column; + padding: 0.25rem 0.6rem; + } `; const ChartTileHeader = styled.span` @@ -46,7 +67,6 @@ interface PriceDisplay { } const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, network }) => { - const theme = useTheme(); const { blocks } = useBlocks(); const [price, setPrice] = useState(0); const [priceList, setPriceList] = useState([0]); @@ -123,20 +143,31 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw }, [blocks, feeSelectedRange, currency]); useEffect(() => { - const hours = Number(priceSelectedRange.slice(0, priceSelectedRange.indexOf(' '))) - const usedPrices = priceList.slice(priceList.length - hours); if (network !== 'mainnet') { setPrice(0); + setPriceList([0]); } else { - const url = `https://bitpay.com/rates/${currency}/usd`; - fetcher(url) + fetcher(`https://bitpay.com/rates/${currency}/usd`) .then(({data}: PriceDetails) => { setPrice(data.rate); }) .catch(() => { setError('Error fetching price. Please try again later.'); }); + + fetcher(`https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`) + .then((priceDisplay: PriceDisplay) => { + setPriceList(priceDisplay.data[0].priceDisplay); + }) + .catch(() => { + setError('Error fetching price graph data. Please try again later.'); + }); } + }, [currency]); + + useEffect(() => { + const hours = Number(priceSelectedRange.slice(0, priceSelectedRange.indexOf(' '))) + const usedPrices = priceList.slice(priceList.length - hours); const priceChartData = { labels: usedPrices, @@ -183,18 +214,7 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw return () => { priceChartInstanceRef.current?.destroy(); }; - }, [priceList, price, priceSelectedRange, currency]); - - useEffect(() => { - const url = `https://bitpay.com/currencies/prices?currencyPairs=["${currency}:USD"]`; - fetcher(url) - .then((priceDisplay: PriceDisplay) => { - setPriceList(priceDisplay.data[0].priceDisplay); - }) - .catch(() => { - setError('Error fetching price graph data. Please try again later.'); - }); - }, [currency]); + }, [priceList, priceSelectedRange, currency]); return (
@@ -204,39 +224,36 @@ const ChainHeader: FC<{ currency: string; network: string }> = ({ currency, netw alt={currency} style={{height: '25px', marginBottom: '0.25rem'}} /> -
-
- - - { error ? : - <> - {getName(currency)} Exchange Rate -
- ${price.toLocaleString()} - -
- -
- -
- - } -
- { hasFees && - - {getName(currency)} Fee + + + { error ? : + <> + {getName(currency)} Exchange Rate
- {blocks?.at(0)?.feeData?.median.toLocaleString()} sats/byte - + ${price.toLocaleString()} +
- +
- +
-
+ } -
-
+ + { hasFees && + + {getName(currency)} Fee +
+ {blocks?.at(0)?.feeData?.median.toLocaleString()} sats/byte + +
+ +
+ +
+
+ } +
); }; diff --git a/packages/insight/src/components/dropdown.tsx b/packages/insight/src/components/dropdown.tsx index 7bc68250003..2c962665682 100644 --- a/packages/insight/src/components/dropdown.tsx +++ b/packages/insight/src/components/dropdown.tsx @@ -1,7 +1,7 @@ -import { FC, CSSProperties } from 'react'; +import {FC, CSSProperties} from 'react'; import ArrowDown from '../assets/images/arrow-down.svg'; import ArrowDownBlack from '../assets/images/arrow-down-black.svg'; -import { useTheme } from 'styled-components'; +import {useTheme} from 'styled-components'; const Dropdown: FC<{ options: string[], diff --git a/packages/insight/src/components/info-card.tsx b/packages/insight/src/components/info-card.tsx index e99bc4f9f22..48a39dc0af7 100644 --- a/packages/insight/src/components/info-card.tsx +++ b/packages/insight/src/components/info-card.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react'; +import {FC} from 'react'; import CopyText from './copy-text'; import styled from 'styled-components'; diff --git a/packages/insight/src/pages/blocks.tsx b/packages/insight/src/pages/blocks.tsx index 9ba09d99c70..7c855fd8ef9 100644 --- a/packages/insight/src/pages/blocks.tsx +++ b/packages/insight/src/pages/blocks.tsx @@ -2,7 +2,7 @@ import BlockList from 'src/components/block-list'; import React, {useEffect, useState} from 'react'; import ChainHeader from '../components/chain-header'; import {useParams} from 'react-router-dom'; -import {useDispatch} from 'react-redux'; +import {useAppDispatch} from 'src/utilities/hooks'; import {changeCurrency, changeNetwork} from 'src/store/app.actions'; import {getApiRoot, normalizeParams} from 'src/utilities/helper-methods'; import {fetcher} from 'src/api/api'; @@ -12,15 +12,21 @@ import {useBlocks} from 'src/contexts'; const Blocks: React.FC = () => { let {currency, network} = useParams<{currency: string; network: string}>(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { blocks, setBlocks } = useBlocks(); const [error, setError] = useState(''); useEffect(() => { + if (!currency || !network) return; nProgress.start(); - if (!currency || !network) - return; + const _normalizeParams = normalizeParams(currency, network); + currency = _normalizeParams.currency; + network = _normalizeParams.network; + + dispatch(changeCurrency(currency)); + dispatch(changeNetwork(network)); + Promise.all([fetcher(`${getApiRoot(currency)}/${currency}/${network}/block?limit=200`)]) .then(([data]) => { setBlocks(data); @@ -31,16 +37,6 @@ const Blocks: React.FC = () => { .finally(() => { nProgress.done(); }); - }, []); - - useEffect(() => { - if (!currency || !network) return; - const _normalizeParams = normalizeParams(currency, network); - currency = _normalizeParams.currency; - network = _normalizeParams.network; - - dispatch(changeCurrency(currency)); - dispatch(changeNetwork(network)); }, [currency, network]); if (!currency || !network) return null; diff --git a/packages/insight/src/utilities/helper-methods.ts b/packages/insight/src/utilities/helper-methods.ts index 8c26f8d7219..ff0aedaa389 100644 --- a/packages/insight/src/utilities/helper-methods.ts +++ b/packages/insight/src/utilities/helper-methods.ts @@ -205,44 +205,4 @@ export const getDifficultyFromBits = (bits: number) => { const maxBody = Math.log(0x00ffff); const scaland = Math.log(256); return Math.exp(maxBody - Math.log(bits & 0x00ffffff) + scaland * (0x1d - ((bits & 0xff000000) >> 24))); -} - -/** - * Merges the source object into the destination object. - * For each property in the source object: - * It sets the destination object property to the source property unless - * both properties are object and the destination object property is not an array. - * - * @param object destination object - * @param source source object - */ -export const merge = (dest: TDest, src: TSrc): TDest & TSrc => { - for (const key in src) { - const destProp = dest !== undefined ? (dest as any)[key] : undefined; - const srcProp = src[key]; - let result; - if (srcProp instanceof Object && destProp instanceof Object && !Array.isArray(destProp)) { - result = merge(destProp, srcProp); - } else { - result = srcProp; - } - (dest as any)[key] = result; - } - return dest as TDest & TSrc; -} -export default merge; - -export const darkenHexColor = (hex: string, amount: number) => { - hex = hex.replace(/^#/, ''); - - let r = parseInt(hex.substring(0, 2), 16); - let g = parseInt(hex.substring(2, 4), 16); - let b = parseInt(hex.substring(4, 6), 16); - - r = Math.max(0, r - amount); - g = Math.max(0, g - amount); - b = Math.max(0, b - amount); - - const toHex = (v: number) => v.toString(16).padStart(2, '0'); - return `#${toHex(r)}${toHex(g)}${toHex(b)}`; } \ No newline at end of file