Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
0e6c23f
create chain page and added header with link to blocks
MicahMaphet Jun 28, 2025
8ff3057
Merge branch 'master' into chain-details
MicahMaphet Jul 8, 2025
0150419
added block list to chain page
MicahMaphet Jul 8, 2025
56ba11e
[REF] replace oneinch with coingecko for token lists
gabrielbazan7 Jul 7, 2025
180c2a1
v10.10.6
nitsujlangston Jul 8, 2025
6bcac67
prevent redefine _getX/Y
Sayrix Jul 1, 2025
ce76c31
verify prePublishRaw tx
leolambo Jul 3, 2025
6ccb7d9
Adding sourceAddress for SPL proposals
leolambo Jul 3, 2025
46ebbd7
add all recipients by default
leolambo Jul 3, 2025
b324db6
feedback
leolambo Jul 3, 2025
8c2b6bb
v10.10.7
nitsujlangston Jul 21, 2025
c5506ee
feat(templates): removes horizontal rule from email
mahume Jul 15, 2025
9a891a6
[FIX] select inputs when replaceTxByFee - reuse one original input
gabrielbazan7 Jul 21, 2025
f77b083
removed unused lodash imports
MicahMaphet Jul 14, 2025
e5dcf28
v10.10.8
nitsujlangston Jul 22, 2025
cd2a8ae
add feePerKb to Solana getFee
leolambo Jul 25, 2025
06bee9f
add solana base fee to defaults
leolambo Jul 25, 2025
3814bee
lint
leolambo Jul 25, 2025
2f8941f
v10.10.9
nitsujlangston Jul 25, 2025
1ae8b3b
Merge branch 'master' of https://github.com/bitpay/bitcore into chain…
MicahMaphet Jul 29, 2025
ec13fa5
moved block list (now 10 blocks) to the right and linting
MicahMaphet Jul 29, 2025
fc181a1
Merge branch 'fee-data' into chain-details
MicahMaphet Jul 29, 2025
aa43033
added view blocks buton to bottom of transactions and fee data block
MicahMaphet Jul 29, 2025
158b008
replaced table with block chain
MicahMaphet Jul 29, 2025
09435bd
added fee chart
MicahMaphet Aug 7, 2025
cf28776
refactored chain-details page and added block tip fee
MicahMaphet Aug 8, 2025
b332934
Merge branch 'master' of https://github.com/bitpay/bitcore into chain…
MicahMaphet Aug 8, 2025
554f259
added median fee to block data display (WIP: removing Promise nesting…
MicahMaphet Aug 11, 2025
dd837ef
refactored nested promises to use feeData on block route
MicahMaphet Sep 23, 2025
3120ea0
added price labels to chart and aesthetic changes
MicahMaphet Sep 27, 2025
8956bd6
removed y title from price chart
MicahMaphet Sep 29, 2025
cbd7484
added fee data graph
MicahMaphet Sep 29, 2025
e1552a7
started 'idle' version of block sample expand viewer
MicahMaphet Sep 29, 2025
0feaabb
added data expansion to the currency block rows
MicahMaphet Oct 2, 2025
edada1f
moved currency colored chip label to top when expanded
MicahMaphet Oct 2, 2025
f5f7cb5
added sats/byte to fee data using new FeeBox element
MicahMaphet Oct 6, 2025
8b74526
replaced boxes with tables broken up into two columns for most data e…
MicahMaphet Oct 7, 2025
5f7cee3
made some text not bold
MicahMaphet Oct 7, 2025
eac648e
added block chip darken on hover
MicahMaphet Oct 7, 2025
b392e21
incorporated background darkening into the date of the BlockChipHeader
MicahMaphet Oct 7, 2025
05569e4
added light mode
MicahMaphet Oct 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/insight/src/Routing.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {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'));
Expand All @@ -12,6 +13,7 @@ function Routing() {
<Suspense>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/:currency/:network/chain' element={<Chain />} />
<Route path='/:currency/:network/blocks' element={<Blocks />} />
<Route path='/:currency/:network/block/:block' element={<Block />} />
<Route path='/:currency/:network/tx/:tx' element={<TransactionHash />} />
Expand Down
6 changes: 6 additions & 0 deletions packages/insight/src/assets/images/block-group-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions packages/insight/src/assets/images/block-group-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 1 addition & 3 deletions packages/insight/src/components/block-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,7 @@ const BlockDetails: FC<BlockDetailsProps> = ({currency, network, block}) => {

<SharedTile
title='Block Reward'
description={`${getConvertedValue(summary.reward, currency).toFixed(
3,
)} ${currency}`}
description={`${getConvertedValue(summary.reward, currency).toFixed(3)} ${currency}`}
/>
<SharedTile title='Confirmations' description={summary.confirmations} />

Expand Down
220 changes: 220 additions & 0 deletions packages/insight/src/components/block-sample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import React, {FC, useState} from 'react';
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';
import styled, {CSSProperties, useTheme} from 'styled-components';
import {colorCodes} from 'src/utilities/constants';
import DataBox from './data-box';

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]};

&: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]};
}

&:hover {
.block-chip-date {
background-color: ${({currency}) => darkenHexColor(colorCodes[currency], 25)};
}
}
`

const FeeBox: FC<{label: string, value: string}> = ({label, value}) => {
return (
<DataBox label={label} colorDark='#333' colorLight='#ccc'>
<>
<div style={{display: 'flex', flexDirection: 'row', alignItems: 'center', width: 'fit-content'}}>
<span style={{fontWeight: 'bold'}}>{value}</span>
<div style={{width: 'fit-content', display: 'flex', flexDirection: 'column', lineHeight: 1, padding: 0, marginLeft: '3px', marginRight: '-2px'}}>
<b style={{fontSize: '9px', color: 'gray'}}>sats</b>
<b style={{
borderTop: '1px solid #888',
fontSize: '9px',
color: 'gray',
textAlign: 'center',
marginTop: '1px'
}}>byte</b>
</div>
</div>
</>
</DataBox>
)
}

const DataRow: FC<{label: string, value: any, style?: CSSProperties}> = ({label, value, style}) => {
return (
<div style={{justifyContent: 'space-between', width: '100%', display: 'flex', margin: '2px', padding: '6px', borderBottom: '1px solid #444', ...style}}>
<b>{label}</b>
<span style={{marginLeft: '8px'}}>{value}</span>
</div>
)
}

const BlockSample: FC<{currency: string, blocksList: BitcoinBlockType[]}> = ({currency, blocksList}) => {
const theme = useTheme();

const BlockGroupIcon: React.FC<{height?: string | number}> = ({height}) => {
return (
<img src={theme.dark ? BlockGroupLightSvg : BlockGroupDarkSvg}
style={{height: (height ? height : '1.5rem')}}/>
);
}

const [expandedBlocks, setExpandedBlocks] = useState<number[]>([]);

if (!blocksList?.length) return null;
return (
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
{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 (
<React.Fragment key={index}>
<div style={{display: 'flex', flexDirection: 'row', gap: '5px', alignItems: 'center', width: '100%'}}>
{
!expandedBlocks.includes(height) &&
<BlockChip currency={currency} onClick={
() => setExpandedBlocks([...expandedBlocks, height])}>
<BlockGroupIcon />
<b>{height}</b>
<BlockGroupIcon />
</BlockChip>
}
{
expandedBlocks.includes(height) ?
<BlockDataBox>
<div style={{display: 'flex', flexWrap: 'wrap', }}>
<div style={{width: '100%', display: 'flex', justifyContent: 'center', marginTop: '-10px'}}>
<BlockChipHeader
currency={currency}
onClick={() => setExpandedBlocks(expandedBlocks.filter(h => h !== height))}
>
<div style={{display: 'flex', alignItems: 'center', gap: '0.5rem'}}>
<BlockGroupIcon height={'2rem'}/>
<b style={{fontSize: '20px'}}>{height}</b>
<BlockGroupIcon height={'2rem'}/>
</div>
<div
className='block-chip-date'
style={{
width: 'fit-content',
height: 'fit-content',
font: 'menu',
fontSize: '10px',
marginTop: '-17px',
padding: '2px',
paddingBottom: 0}}>
{getFormattedDate(time)}
</div>
</BlockChipHeader>
</div>
<div style={{width: '50%', display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', borderRight: '1px solid #555'}}>
<div style={{margin: '8px 4px'}}>
<DataRow label='Transaction Count' value={transactionCount}/>
<DataRow label='Size (kB)' value={size / 1000}/>
<DataRow label='Confirmations' value={confirmations}/>
<DataRow label='Version' value={version}/>
</div>
<div style={{margin: '8px 4px'}}>
<DataRow label='Reward' value={`${getConvertedValue(reward, currency).toFixed(4)} ${currency}`}/>
<DataRow label='Difficulty' value={getDifficultyFromBits(bits)}/>
<DataRow label='Bits' value={bits}/>
<DataRow label='Nonce' value={nonce}/>
</div>
<DataBox label='Fee Data' style={{borderWidth: '3px', borderColor: theme.dark ? '#333' : '#ccc', backgroundColor: theme.dark ? '#111' : '#f0f0f0', margin: '0.5rem 1rem'}} centerLabel>
<>
<div style={{margin: '-1rem 0 0 0'}}>
<div style={{display: 'flex', marginTop: '0.5rem'}}>
<FeeBox label={'Mean'} value={mean.toFixed(4)} />
<FeeBox label={'Median'} value={median.toFixed(4)} />
</div>
<div style={{display: 'flex', marginTop: '-0.5rem'}}>
<FeeBox label={'Mode'} value={mode.toFixed(4)} />
<DataBox label='Total' colorDark='#333' colorLight='#ccc'>
<>
<b>{getConvertedValue(feeTotal, currency).toFixed(3)}</b>
<b style={{fontSize: '12px', color: 'gray'}}> {currency}</b>
</>
</DataBox>
</div>
</div>
</>
</DataBox>
<div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'center'}}>
<DataBox label='Hash' style={{backgroundColor: theme.dark ? '#222': '#e5e5e5'}} centerLabel>{hash}</DataBox>
<DataBox label='Merkle Root' style={{backgroundColor: theme.dark ? '#222' : '#e5e5e5'}} centerLabel>{merkleRoot}</DataBox>
</div>
</div>
</div>
</BlockDataBox>
:
<BlockDataBox>
<b style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
}}>
<span>{getFormattedDate(time)}</span>
<span>{transactionCount} transactions</span>
<span>{size / 1000} kB</span>
<span>~{median?.toFixed(4)} sats/Byte</span>
</b>
</BlockDataBox>
}
</div>
{index !== blocksList.length - 1 && (
<div style={{display: 'flex', flexDirection: 'row', width: '100%', alignItems: 'center', justifyContent: 'flex-start'}}>
<div
style={{
width: '4px',
height: '20px',
borderRadius: '1px',
background: colorCodes[currency],
marginLeft: '70px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
/>
</div>
)}

</React.Fragment>
);
})}
</div>
);
};

export default BlockSample;
89 changes: 89 additions & 0 deletions packages/insight/src/components/chain-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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';

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<HTMLCanvasElement | null>(null);
const chartInstanceRef = useRef<ChartJS | null>(null);

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 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,
};

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 (
<div style={{borderBottom: '1px solid', padding: '0 5px', height: 'fit-content', marginBottom: '0.5rem'}}>
<div style={{display: 'flex'}}>
<img
src={`https://bitpay.com/img/icon/currencies/${currency}.svg`}
alt={currency}
style={{height: '100px'}}
/>
{priceList.length > 0 && (
<div style={{height: '200px', width: '100%', minWidth: 0}}>
<canvas ref={chartRef} aria-label='price line chart' role='img' />
</div>
)}
</div>
<div style={{display: 'flex', justifyContent: 'space-around'}}>
<span style={{margin: '0 10px'}}>{price} USD </span>
</div>
</div>
);
};

export default ChainHeader;
6 changes: 3 additions & 3 deletions packages/insight/src/components/currency-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,12 @@ const CurrencyTile: FC<CurrencyTileProps> = ({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 (
<CurrencyTileDiv currency={currency} onClick={gotoAllBlocks} key={currency}>
<CurrencyTileDiv currency={currency} onClick={gotoChain} key={currency}>
<CurrencyTileHeader>
<img src={imgSrc} width={35} height={35} alt={`${currency} logo`} />
<div>
Expand Down
Loading