Documentation Index
Fetch the complete documentation index at: https://mintlify.com/satsigner/satsigner/llms.txt
Use this file to discover all available pages before exploring further.
Built-in Blockchain Explorer
SatSigner includes a powerful built-in blockchain explorer that works with your configured Electrum or Esplora backend. Explore blocks, visualize difficulty adjustments, and analyze Bitcoin’s blockchain without relying on third-party websites.
Overview
The explorer provides:
- Block Explorer: Fetch and view detailed block information
- Difficulty Visualization: Interactive spiral visualization of mining epochs
- Transaction Browser: View transactions within blocks
- Network Statistics: Real-time blockchain metrics
- Privacy-First: Uses your own node or configured backend
Block Explorer
Features
View comprehensive block data:
type Block = {
id: string // Block hash
height: number // Block height
merkle_root?: string // Merkle root
previousblockhash?: string // Previous block hash
timestamp: number // Unix timestamp
weight: number // Block weight
size: number // Block size (bytes)
version: number // Block version
nonce: number // Mining nonce
difficulty: number // Difficulty target
tx_count?: number // Transaction count
mediantime?: number // Median time past
}
Implementation
Location: apps/mobile/app/(authenticated)/(tabs)/(signer,explorer,converter)/explorer/block/index.tsx:1
Fetching Blocks (Esplora)
// apps/mobile/app/.../explorer/block/index.tsx:56
async function fetchBlockEsplora(height: number): Promise<Block> {
const esplora = new Esplora(backendUrl)
const blockHash = await esplora.getBlockAtHeight(height)
const block = await esplora.getBlockInfo(blockHash)
return block
}
Esplora API Endpoints:
/block-height/{height} - Get block hash at height
/block/{hash} - Get block details
Fetching Blocks (Electrum)
// apps/mobile/app/.../explorer/block/index.tsx:63
async function fetchBlockElectrum(height: number): Promise<Block> {
const electrum = await ElectrumClient.initClientFromUrl(backendUrl)
const block = await electrum.getBlock(height)
electrum.close()
return {
id: block.getId(),
height,
merkle_root: block.merkleRoot?.toString('hex'),
previousblockhash: block.prevHash?.toString('hex'),
timestamp: block.timestamp,
weight: block.weight(),
size: block.weight() * 4, // Size = Weight / 4
version: block.version,
nonce: block.nonce,
difficulty: getDifficultyFromBits(block.bits),
tx_count: block.transactions?.length,
mediantime: undefined
}
}
Difficulty Calculation
Convert compact bits format to difficulty:
// apps/mobile/app/.../explorer/block/index.tsx:22
function getDifficultyFromBits(bits: number): number {
const exponent = bits >>> 24
const mantissa = bits & 0x007fffff
let target = BigInt(mantissa)
const shift = 8 * (exponent - 3)
if (shift >= 0) {
target = target * (BigInt(1) << BigInt(shift))
} else {
target = target / (BigInt(1) << BigInt(-shift))
}
const maxTarget = BigInt(
'0x00000000ffff0000000000000000000000000000000000000000000000000000'
)
return Number(maxTarget) / Number(target)
}
Block Navigation
Navigate between blocks:
// apps/mobile/app/.../explorer/block/index.tsx:115
function nextBlockHeight() {
setInputHeight(Math.min(maxBlockHeight, Number(inputHeight) + 1).toString())
}
function prevBlockHeight() {
setInputHeight(Math.max(1, Number(inputHeight) - 1).toString())
}
async function fetchLatestBlock() {
const tipHeight = await getBlockchainHeight('bitcoin')
setMaxBlockHeight(tipHeight)
await fetchBlock(tipHeight)
}
User Interface
The block explorer UI provides:
- Height Input: Jump to specific block height
- Navigation Buttons: Previous/Next block
- Latest Block Button: Fetch blockchain tip
- Block Details: All block metadata displayed
- Transaction List: View transactions in block
Difficulty Adjustment Visualizer
Overview
Visualize Bitcoin’s mining difficulty over time with an interactive spiral representation of each difficulty epoch (2016 blocks).
Location: apps/mobile/app/(authenticated)/(tabs)/(signer,explorer,converter)/explorer/difficulty.tsx:1
Difficulty Epochs
const BLOCKS_PER_EPOCH = 2016
type BlockDifficulty = {
height: number // Block height
timestamp: number // Block timestamp
txCount: number // Transaction count
chainWork: string // Cumulative work
nonce: number // Mining nonce
size: number // Block size
weight: number // Block weight
cycleHeight: number // Position in epoch (0-2015)
timeDifference: number // Time since previous block
}
Fetching Epoch Data
// apps/mobile/app/.../explorer/difficulty.tsx:93
async function fetchData(epoch: number) {
const fileName = getFileName(epoch)
const response = await fetch(DATA_LINK + fileName)
const rawData = await response.json() as DifficultyEpochsData[][]
const items = rawData[0]
const data = items.map(value => ({
height: value[0].height,
timestamp: value[1].time,
txCount: value[2].nTx,
chainWork: value[3].chainwork,
nonce: value[4].nonce,
size: value[5].size,
weight: value[6].weight,
cycleHeight: value[7].block_in_cycle,
timeDifference: value[8].time_difference
}) as BlockDifficulty)
setData(data)
}
Current Difficulty Stats
Fetch real-time difficulty adjustment data:
// apps/mobile/app/.../explorer/difficulty.tsx:68
async function fetchDifficultyAdjustment() {
const response = await mempoolOracle.getDifficultyAdjustment()
// Average block time
const avgTimeInSeconds = response.timeAvg / 1000
const avgTimeInMinutes = avgTimeInSeconds / 60
setAverageBlockTime(`~${avgTimeInMinutes.toFixed(1)} minutes`)
// Time to next adjustment
const [time, timeUnit] = formatTimeFromNow(response.remainingTime)
setRemainingTime(`~${time.toFixed(1)} ${timeUnit}`)
}
Displayed Metrics:
- Average block time for current epoch
- Time remaining until next difficulty adjustment
- Current epoch number
- Epoch date range
- Block height range
Spiral Visualization
Each epoch is displayed as a spiral:
const CANVAS_WIDTH = SCREEN_WIDTH
const CANVAS_HEIGHT = 0.7 * SCREEN_HEIGHT
<SSSpiralBlocks
data={data} // Block data array
loading={loading} // Loading state
maxBlocksPerSpiral={BLOCKS_PER_EPOCH} // 2016 blocks
canvasWidth={CANVAS_WIDTH}
canvasHeight={CANVAS_HEIGHT}
onBlockPress={selectBlock} // Select block for details
/>
Visual Encoding:
- Spiral position: Block order in epoch
- Color: Block properties (time, size, etc.)
- Tap interaction: View block details
Block Details Modal
Tapping a block shows detailed information:
// apps/mobile/app/.../explorer/difficulty.tsx:263
function BlockDetails({ block }: BlockDetailsProps) {
return (
<>
<SSText>Height: {block.height}</SSText>
<SSText>Cycle Height: {block.cycleHeight}</SSText>
<SSText>Transactions: {block.txCount}</SSText>
<SSText>Size: {block.size} bytes</SSText>
<SSText>VSize: {Math.trunc(block.weight / 4)}</SSText>
<SSText>Weight: {block.weight} WU</SSText>
<SSText>Nonce: {block.nonce}</SSText>
<SSText>Date: {formatDate(block.timestamp * 1000)}</SSText>
<SSText>Time Difference: {block.timeDifference}s</SSText>
<SSText>Chain Work: {block.chainWork}</SSText>
</>
)
}
Epoch Navigation
Navigate between difficulty adjustment periods:
// Navigate to specific epoch
function getFileName(index: number) {
return `rcp_bitcoin_block_data_${(index * BLOCKS_PER_EPOCH)
.toString()
.padStart(7, '0')}.json`
}
// Epoch 0: Blocks 0-2015
// Epoch 1: Blocks 2016-4031
// Epoch 426: Blocks 860,000+
Transaction Explorer
Viewing Block Transactions
Location: apps/mobile/app/(authenticated)/(tabs)/(signer,explorer,converter)/explorer/block/[block]/transactions.tsx:1
List all transactions in a block:
type BlockTransaction = {
txid: string
version: number
locktime: number
vin: Array<{
txid: string
vout: number
sequence: number
scriptsig?: string
witness?: string[]
}>
vout: Array<{
scriptpubkey: string
scriptpubkey_address?: string
value: number
}>
size: number
weight: number
fee?: number
status: {
confirmed: boolean
block_height: number
block_hash: string
block_time: number
}
}
Backend Integration
Supported Backends
The explorer works with:
Esplora
- REST API interface
- JSON responses
- Public instances available
- Self-hostable
Electrum
- Electrum protocol
- Binary responses
- Wide server availability
- Lower bandwidth
Backend Configuration
Select backend in Settings → Network:
type BlockchainConfig = {
server: {
backend: 'esplora' | 'electrum'
url: string
}
}
Default Configuration:
- Backend: Esplora
- URL: Public Esplora instance or custom node
Switching Backends
The explorer automatically adapts:
// apps/mobile/app/.../explorer/block/index.tsx:83
async function fetchBlock(height: number) {
setLoading(true)
try {
const block = backend === 'esplora'
? await fetchBlockEsplora(height)
: await fetchBlockElectrum(height)
setBlock(block)
setInputHeight(height.toString())
return block
} catch {
toast.error(`Failed to fetch block ${height}`)
} finally {
setLoading(false)
}
}
Privacy Considerations
Using Your Own Node
Best Practice: Connect to your own node
- No data leaked to third parties
- Complete privacy for block lookups
- No rate limiting
- Enhanced security
Third-Party Backends
When using public backends:
- Block lookups are visible to server
- IP address exposed
- Consider using Tor (see Privacy Tools)
- Avoid patterns that leak wallet info
Data Minimization
- Only fetch blocks you need
- Don’t explore blocks related to your transactions
- Use Tor for additional privacy
- Self-host backend when possible
Caching Strategy
// Cache recently viewed blocks
const blockCache = new Map<number, Block>()
async function fetchBlock(height: number) {
if (blockCache.has(height)) {
return blockCache.get(height)
}
const block = await fetchBlockFromBackend(height)
blockCache.set(height, block)
// Limit cache size
if (blockCache.size > 100) {
const firstKey = blockCache.keys().next().value
blockCache.delete(firstKey)
}
return block
}
Lazy Loading
- Transactions loaded on demand
- Difficulty epochs fetched as needed
- Progressive rendering for large datasets
Use Cases
Blockchain Analysis
- Study Bitcoin’s block structure
- Analyze mining patterns
- Research difficulty adjustments
- Educational exploration
Transaction Verification
- Verify transaction inclusion
- Check confirmation count
- Inspect transaction details
- Validate payment proofs
Mining Research
- Study block timing distributions
- Analyze difficulty trends
- Research orphan rates
- Monitor network hashrate
Future Enhancements
Planned features:
- Mempool Explorer: View unconfirmed transactions
- Address Lookup: Search any Bitcoin address
- UTXO Analysis: Visualize UTXO set
- Fee Estimation: Historical fee rate charts
- Network Graphs: Visualize network topology
- Script Decoder: Parse and explain Bitcoin scripts
Implementation Reference
Block Explorer: apps/mobile/app/(authenticated)/(tabs)/(signer,explorer,converter)/explorer/block/index.tsx:1
Difficulty Visualizer: apps/mobile/app/(authenticated)/(tabs)/(signer,explorer,converter)/explorer/difficulty.tsx:1
Esplora API: apps/mobile/api/esplora.ts:1
Electrum Client: apps/mobile/api/electrum.ts:1
Blockchain Types: apps/mobile/types/models/Blockchain.ts:1
Resources