区块链 区块链技术 比特币公众号手机端

如何构建一个实时比特币钱包分析应用

liumuhui 3小时前 阅读数 1 #区块链

概述

在数字货币正在重塑金融交易的时代,跟踪和分析 Bitcoin 交易的能力变得至关重要。这份全面的指南专为经常询问如何查看 Bitcoin 余额历史或查找 Bitcoin 地址余额的开发者和爱好者而设计。它提供了一个详细的分步教程,教你如何使用 Blockbook for Bitcoin 构建一个实时 Bitcoin 钱包分析应用。

我们的应用满足了加密货币社区中的一个常见需求:不仅能够查看 Bitcoin 钱包的当前余额,还能探索其历史交易和余额变化。对于希望了解钱包随时间变化活动的用户和开发者来说,这个实时分析工具极具价值,能够提供对趋势、交易模式以及 Bitcoin 地址整体财务轨迹的深入洞察。

无论你是想增强个人项目、在专业应用中提供更多价值,还是只是对 Bitcoin 钱包的复杂运作感到好奇,这份指南都可以作为你的起点。让我们踏上这段旅程,释放实时 Bitcoin 钱包分析的潜力。

你将做什么

  • 了解 BTC Blockbook JSON-RPC 以及如何使用它
  • 在 Quicknode 上创建一个 Bitcoin 端点
  • 创建一个 React 应用,用于获取 Bitcoin 地址的交易数据,包括实时和历史余额

你需要什么

  • 一个 Quicknode 账户(在此免费注册)
  • 已安装 Node.js
  • 具备 JavaScript 和 React 的使用经验
  • 一个代码编辑器(例如 VScode)
依赖 版本
node.js latest

Blockbook 能力概述

BTC Blockbook JSON-RPC 是一个强大的后端工具,用于索引区块链交易,并提供全面的 RPC 接口,以便与 Bitcoin 等 UTXO 链交互。它的突出特点包括:

  • 高性能:快速处理大量数据,实时提供准确信息。
  • 丰富的数据集:提供关于交易、地址和余额的详细信息。
  • 易用性:通过清晰的文档和社区支持简化集成过程。

通过利用 Blockbook 的能力,开发者可以克服常见的数据检索挑战,并创建为用户提供最新钱包信息和市场数据的应用。

Blockbook 对开发者的价值

如果没有 Blockbook,开发者将不得不直接与 Bitcoin 区块链交互来获取这些信息。这个过程涉及设置和维护一个全节点、解析原始区块链数据,并持续与最新的网络交易同步。这些任务不仅消耗大量资源,还需要对区块链协议和数据结构有深入的理解。

Blockbook 通过提供一个用户友好的 API 来简化这一过程,该 API 屏蔽了直接与区块链交互的复杂性。它允许开发者查询钱包余额、交易历史和货币汇率,而无需直接管理区块链数据。这种简化的方法使开发者能够更专注于创建功能丰富的应用,而不是区块链数据管理的细节,从而显著加快开发周期并减少技术开销。

截至本文撰写时,Blockbook 包含 10 个强大的 RPC 方法。我们将在本指南中使用其中的三个:

  • bb_getAddress:返回一个地址的余额和交易。
  • bb_getTickers:返回指定货币和日期的货币汇率。如果该特定时间戳下没有该货币,则返回最接近的汇率。
  • bb_getBalanceHistory:返回该地址的余额历史。

设置 Bitcoin 端点

使用 Blockbook RPC 设置你的 Bitcoin 端点非常简单。如果你还没有注册,可以在此创建一个账户。

登录后,导航到 quicknode.com/endpoints 页面,点击 Create an endpoint。选择 Bitcoin mainnet,然后点击 Next。接下来,你会被提示配置附加组件。激活 Blockbook RPC。之后,只需点击 Create Endpoint

如果你已经有一个没有此附加组件的 Bitcoin 端点,请前往你的 Bitcoin 端点中的 Add-ons 页面,选择 Blockbook RPC 并激活它。

Quicknode Endpoints page

当你的端点准备好后,复制 HTTP Provider 链接并妥善保管,因为你在下一节中会用到它。

构建应用

要开始构建我们的 Bitcoin 钱包分析应用,你首先需要设置好你的开发环境。如果你还没有安装,请在计算机上安装 Node.js 和 npm。Node.js 将运行你的开发服务器,npm 将管理项目的依赖。

设置项目

步骤 1:设置 React 应用

使用 npx create-react-app 来搭建一个新的 React 项目:

npx create-react-app bitcoin-analytics
cd bitcoin-analytics

步骤 2:安装所需依赖

安装 Axios 用于发起 HTTP 请求,Chart.js 及其 React 封装用于绘制图表,Tailwind CSS 用于样式,DatePicker 日历组件用于选择日期以查看历史余额。

npm install axios react-chartjs-2 chart.js tailwindcss chartjs-adapter-moment
npm install react-datepicker --save

步骤 3:配置 Tailwind CSS

运行以下命令来安装和配置 Tailwind CSS,它将帮助我们为应用设置样式:

npm install -D tailwindcss
npx tailwindcss init

tailwind.config.js 文件中配置模板文件。

/** @type {import('tailwindcss').Config} */
module.exports = {
	content: ['./src/**/*.{js,jsx,ts,tsx}'],
	theme: {
		extend: {},
	},
	plugins: [],
}

将 @tailwind 指令添加到 .src/index.css 文件的顶部。

@tailwind base;
@tailwind components;
@tailwind utilities;

构建项目

设计应用结构

在开始编码之前,规划应用的结构始终是一个好习惯。对于本指南中将构建的 Bitcoin 钱包分析应用,你需要 AddressInputBalanceChartWalletStats 等组件。

如下方图片所示,你将在本应用中使用 bb_getAddressbb_getTickersbb_getBalanceHistory 方法。

应用的操作流程如下:

  • 用户输入一个 Bitcoin 地址并按回车键。
  • 通过 bb_getAddress 方法获取地址的余额和交易数据。 通过 bb_getTickers 方法获取 Bitcoin 与不同法币的实时价格。
  • 地址数据和实时 Bitcoin 价格数据被传入 WalletStats 组件。
  • 用户选择开始日期、结束日期和时间间隔,以获取地址的历史 BTC 余额变化。
  • 通过 bb_getBalanceHistory 方法获取历史 BTC 余额数据。
  • 数据被传入 BalanceChart 组件。

App Overview with Used Methods

现在,让我们开始编码。

步骤 0:创建必要的文件和文件夹

src 文件夹中创建 componentsapi 文件夹。然后,在 api 文件夹中创建 fetchData.js 文件,并在 components 文件夹中创建 AddressInput.jsBalanceChart.jsWalletStats.js 文件。你可以通过在项目目录的终端中运行以下命令一次性完成所有这些操作。

mkdir src/api && mkdir src/components
echo > src/api/fetchData.js && echo > src/components/AddressInput.js && echo > src/components/BalanceChart.js && echo > src/components/WalletStats.js

步骤 1:Bitcoin 地址输入组件

AddressInput 组件包含一个用于输入 Bitcoin 地址的表单。当表单提交时,它会将当前地址状态作为参数,传递给通过 props 传入的 onAddressSubmit 函数。

这是一个自包含的表单组件,供用户输入 Bitcoin 地址,然后可用于获取余额和交易数据。

用你的代码编辑器打开 src/components/AddressInput.js 文件,并按如下方式修改。

// src/components/AddressInput.js

import React, { useState } from 'react'

// 用于输入 Bitcoin 地址的组件,在表单提交时调用 onAddressSubmit prop。
const AddressInput = ({ onAddressSubmit }) => {
	// 用于存储和设置 Bitcoin 地址的状态Hook
	const [address, setAddress] = useState('')

	// 处理表单提交的函数
	const handleSubmit = e => {
		e.preventDefault()
		onAddressSubmit(address)
	}

	return (
		// 表单容器,带有居中对齐和内边距的样式
		<div className="flex flex-col items-center justify-center p-4">
			<form onSubmit={handleSubmit} className="w-full max-w-md">
				<div className="mb-2">
					<label
						htmlFor="address"
						className="block text-gray-700 text-lg font-bold mb-2"
					>
						Balance and Chart of Bitcoin Addresses
					</label>
					<div className="mt-1">
						<input
							type="text"
							id="address"
							value={address}
							onChange={e => setAddress(e.target.value)}
							className="shadow appearance-none border rounded w-full py-3 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
							placeholder="Enter Bitcoin address"
						/>
					</div>
				</div>
				<div className="flex justify-center">
					<button
						type="submit"
						className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-orange-400 hover:bg-orange-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
					>
						Submit
					</button>
				</div>
			</form>
		</div>
	)
}

export default AddressInput

步骤 2:WalletStats 组件

WalletStats 组件显示各种 Bitcoin 钱包统计信息,包括其余额及所选法币的等值金额、总收到和总发送的 BTC 数量、总交易数量以及未确认余额。此外,它还允许切换显示最新的 10 个交易 ID。

用你的代码编辑器打开 src/components/WalletStats.js 文件,并按如下方式修改。

// src/components/WalletStats.js

import React, { useState } from 'react'

// 将 satoshi 单位转换为 Bitcoin,保留 8 位小数
const satoshiToBitcoin = satoshi => (satoshi / 100000000).toFixed(8)

// WalletStats 组件将 walletData 和 fiatRate 作为 props
const WalletStats = ({ walletData, fiatRate }) => {
	// 用于切换交易 ID 显示的状态
	const [showTxids, setShowTxids] = useState(false)
	// 用于选择显示 Bitcoin 等值价值的法币状态
	const [fiatCurrency, setFiatCurrency] = useState('USD')

	if (!walletData) {
		return <p>No data available.</p>
	}

	// 使用所选法币汇率计算钱包余额的法币等值
	const fiatBalance = (
		satoshiToBitcoin(walletData.balance) * fiatRate[fiatCurrency]
	).toLocaleString(undefined, {
		minimumFractionDigits: 2,
		maximumFractionDigits: 2,
	})

	// return 语句包含用于渲染钱包信息的 JSX
	return (
		<div className="p-4 rounded-md shadow-md bg-white my-4">
			<h2 className="text-xl font-bold mb-4">Wallet Information</h2>
			<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
				<div>
					<strong>Address:</strong> {walletData.address}
				</div>
				<div>
					<strong>Balance:</strong> {satoshiToBitcoin(walletData.balance)} BTC (
					{fiatCurrency} {fiatBalance})
				</div>
				<div>
					<strong>Total Received:</strong>{' '}
					{satoshiToBitcoin(walletData.totalReceived)} BTC
				</div>
				<div>
					<strong>Total Sent:</strong> {satoshiToBitcoin(walletData.totalSent)}{' '}
					BTC
				</div>
				<div>
					<strong>Total Transactions:</strong> {walletData.txs}
				</div>
				<div>
					<strong>Unconfirmed Balance:</strong>{' '}
					{satoshiToBitcoin(walletData.unconfirmedBalance)} BTC
				</div>
				<div>
					<strong>Unconfirmed Transactions:</strong> {walletData.unconfirmedTxs}
				</div>
				<div className="flex flex-col md:flex-row items-center mb-4">
					<label
						htmlFor="fiat-currency"
						className="block text-gray-700 font-bold mr-2"
					>
						Select Fiat Currency:
					</label>
					<select
						id="fiat-currency"
						value={fiatCurrency}
						onChange={e => setFiatCurrency(e.target.value)}
						className="block shadow border rounded py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
					>
						<option value="USD">USD</option>
						<option value="EUR">EUR</option>
						<option value="GBP">GBP</option>
					</select>
				</div>
				{walletData.txids && walletData.txids.length > 0 && (
					<div>
						<button
							onClick={() => setShowTxids(!showTxids)}
							className="text-blue-600 hover:underline"
						>
							{showTxids ? 'Hide' : 'Show'} Latest 10 Transactions
						</button>
						{showTxids && (
							<ul className="mt-4">
								{walletData.txids.slice(0, 10).map((txid, index) => (
									<li key={index} className="truncate">
										{txid}
									</li>
								))}
							</ul>
						)}
					</div>
				)}
			</div>
		</div>
	)
}

export default WalletStats

步骤 3:BalanceChart 组件

BalanceChart 组件渲染一个条形图,基于一组历史数据可视化 Bitcoin 余额随时间的变化。

用你的代码编辑器打开 src/components/BalanceChart.js 文件,并按如下方式修改。

// src/components/BalanceChart.js

import React from 'react'
import { Bar } from 'react-chartjs-2'
// Chart.js 及其组件被导入以创建图表
import {
	Chart as ChartJS,
	CategoryScale,
	LinearScale,
	BarElement,
	PointElement,
	Title,
	Tooltip,
	Legend,
	TimeSeriesScale,
} from 'chart.js'
import 'chartjs-adapter-moment' // 时间刻度所需

// 注册创建带时间序列的条形图所需的 Chart.js 组件
ChartJS.register(
	CategoryScale,
	LinearScale,
	BarElement,
	PointElement,
	Title,
	Tooltip,
	Legend,
	TimeSeriesScale
)

const BalanceChart = ({ historyData }) => {
	// 数据处理:将 Satoshi 值转换为 Bitcoin 并创建数据点
	const dataPoints = historyData.map(entry => ({
		t: new Date(entry.time * 1000),
		y:
			(parseInt(entry.received, 10) + // 解析接收到的 Satoshi 字符串并转换为 Bitcoin
				parseInt(entry.sentToSelf, 10) - // 解析并添加发送给自己的 Satoshi,因为它们不改变余额
				parseInt(entry.sent, 10)) / // 解析并减去从钱包发送的 Satoshi
			100000000, // Satoshi 到 Bitcoin 的转换系数
	}))

	// 按时间戳对数据点进行排序
	dataPoints.sort((a, b) => a.t - b.t)

	// 为图表数据集创建标签和数据数组
	const labels = dataPoints.map(dp => dp.t)
	const data = dataPoints.map(dp => dp.y)

	// 配置条形图的数据和外观
	const chartData = {
		labels: labels,
		datasets: [\
			{\
				label: 'Net BTC Balance Change Over Time',\
				data: data,\
				fill: false,\
				backgroundColor: 'rgb(33, 150, 243)',\
				borderColor: 'rgba(33, 150, 243, 0.2)',\
			},\
		],
	}

	const chartOptions = {
		scales: {
			x: {
				type: 'time',
				time: {
					unit: 'day',
				},
				title: {
					display: true,
					text: 'Date',
				},
			},
			y: {
				beginAtZero: false,
				title: {
					display: true,
					text: 'Net BTC Balance Change',
				},
			},
		},
		interaction: {
			intersect: false,
			mode: 'index',
		},
		responsive: true,
		maintainAspectRatio: true,
	}

	return <Bar data={chartData} options={chartOptions} />
}

export default BalanceChart

步骤 4:fetchData 文件

fetchData 文件提供了一组函数,使用 Axios 从 Bitcoin RPC API 获取不同用途的数据:获取地址数据、获取 Bitcoin 法币汇率、获取余额历史。

用你的代码编辑器打开 src/api/fetchData.js 文件,并按如下方式修改。不要忘记将 YOUR_QUICKNODE_ENDPOINT 占位符替换为你的 Quicknode Bitcoin 端点。

// src/api/fetchData.js

import axios from 'axios'

// Quicknode 端点的基础 URL,应替换为实际的端点 URL。
const BASE_URL = 'YOUR_QUICKNODE_ENDPOINT'

// 获取指定 Bitcoin 地址数据的函数
export const getAddressData = async address => {
	const postData = {
		method: 'bb_getaddress', // 获取地址数据的 RPC 方法
		params: [address], // 该方法的参数,这里是 Bitcoin 地址
		id: 1,
		jsonrpc: '2.0',
	}

	try {
		const response = await axios.post(BASE_URL, postData, {
			headers: {
				'Content-Type': 'application/json',
			},
			maxBodyLength: Infinity,
		})
		return response.data.result
	} catch (error) {
		console.error(error)
	}
}

// 获取指定时间戳的 Bitcoin 法币汇率的函数
export const getBitcoinFiatRates = async timestampUnix => {
	const postData = {
		method: 'bb_gettickers', // 获取 Bitcoin 法币汇率的 RPC 方法
		params: [{ timestamp: timestampUnix }], // 带时间戳的参数
		id: 1,
		jsonrpc: '2.0',
	}

	try {
		const response = await axios.post(BASE_URL, postData, {
			headers: {
				'Content-Type': 'application/json',
			},
			maxBodyLength: Infinity,
		})
		return response.data.result.rates
	} catch (error) {
		console.error(error)
	}
}

// 获取 Bitcoin 地址余额历史的函数
export const getBalanceHistory = async (address, from, to, groupBy) => {
	const postData = {
		method: 'bb_getbalancehistory', // 获取余额历史的 RPC 方法
		params: [\
			address, // Bitcoin 地址\
			{\
				from: from.toString(), // 时间范围的开始,以字符串表示\
				to: to.toString(), // 时间范围的结束,以字符串表示\
				fiatcurrency: 'usd', // 获取余额所用的法币\
				groupBy: groupBy, // 余额历史的分组间隔\
			},\
		],
		id: 1,
		jsonrpc: '2.0',
	}

	try {
		const response = await axios.post(BASE_URL, postData, {
			headers: {
				'Content-Type': 'application/json',
			},
			maxBodyLength: Infinity,
		})
		return response.data.result
	} catch (error) {
		console.error(error)
	}
}

步骤 5:主 React 组件 - App.js

App.js 文件是应用的主 React 组件(App)。它导入必要的子组件和 API 函数,管理地址数据、余额历史、日期选择、时间间隔和法币汇率的状态,并提供一个带有头部和主要内容区域的结构化布局,用于用户交互。

用你的代码编辑器打开 src/App.js 文件,并按如下方式修改。

// src/App.js

import React, { useState, useEffect } from 'react'
// 导入子组件和样式

import AddressInput from './components/AddressInput'
import WalletStats from './components/WalletStats'
import BalanceChart from './components/BalanceChart'
import DatePicker from 'react-datepicker'
import 'react-datepicker/dist/react-datepicker.css'
// 用于获取各种数据的 API 函数
import {
	getAddressData,
	getBalanceHistory,
	getBitcoinFiatRates,
} from './api/fetchData'

const App = () => {
	// 用于各种数据和 UI 控制的状态Hook
	const [addressData, setAddressData] = useState(null)
	const [balanceHistory, setBalanceHistory] = useState(null)
	const [startDate, setStartDate] = useState(new Date())
	const [endDate, setEndDate] = useState(new Date())
	// 余额历史的默认时间间隔,以秒为单位
	const [interval, setInterval] = useState('3600') // 默认为 3600 秒(1 小时)
	// 默认法币汇率设置为 0,将通过 API 调用更新
	const [fiatRate, setFiatRate] = useState({ USD: 0, EUR: 0, GBP: 0 })

	// 在组件挂载时获取法币汇率的 useEffect Hook
	useEffect(() => {
		fetchBitcoinFiatRates()
	}, [])

	// 处理地址表单提交的函数
	const handleAddressSubmit = async address => {
		const data = await getAddressData(address)
		setAddressData(data)
	}

	// 根据所选日期和时间间隔获取余额历史的函数
	const fetchBalanceHistory = async () => {
		// 将日期转换为时间戳
		const startTimestamp = Math.floor(startDate.getTime() / 1000)
		const endTimestamp = Math.floor(endDate.getTime() / 1000)

		const history = await getBalanceHistory(
			addressData.address,
			startTimestamp,
			endTimestamp,
			interval
		)
		setBalanceHistory(history)
	}

	// 获取当前 Bitcoin 法币汇率的函数
	const fetchBitcoinFiatRates = async () => {
		// 假设 API 响应结构与之前提供的一致
		const timestampNow = Math.floor(Date.now() / 1000)

		const fiatRates = await getBitcoinFiatRates(timestampNow)
		setFiatRate({ USD: fiatRates.usd, EUR: fiatRates.eur, GBP: fiatRates.gbp })
	}

	return (
		<div className="App bg-background min-h-screen flex flex-col items-center">
			<header className="bg-white shadow w-full">
				<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
					<h1 className="text-3xl font-bold text-gray-900">Bitcoin Explorer</h1>
				</div>
			</header>
			<main className="flex-grow">
				<AddressInput onAddressSubmit={handleAddressSubmit} />
				{addressData && (
					<div>
						<WalletStats walletData={addressData} fiatRate={fiatRate} />
						<div className="container mx-auto p-6">
							<h2 className="text-2xl font-bold text-center my-6">
								Bitcoin Balance Chart
							</h2>

							<div className="flex flex-wrap items-center justify-center gap-4 mb-6 shadow-lg p-6 rounded-lg bg-white">
								<div className="flex flex-col mb-3">
									<label htmlFor="start-date" className="font-semibold mb-2">
										Starting Date
									</label>
									<DatePicker
										id="start-date"
										selected={startDate}
										onChange={date => setStartDate(date)}
										className="w-full max-w-xs h-12 rounded-lg text-lg shadow-inner"
									/>
								</div>
								<div className="flex flex-col mb-3">
									<label htmlFor="end-date" className="font-semibold mb-2">
										Ending Date
									</label>
									<DatePicker
										id="end-date"
										selected={endDate}
										onChange={date => setEndDate(date)}
										className="w-full max-w-xs h-12 rounded-lg text-lg shadow-inner border"
									/>
								</div>
								<div className="flex flex-col mb-3">
									<label htmlFor="time-interval" className="font-semibold mb-2">
										Time Interval
									</label>
									<select
										id="time-interval"
										value={interval}
										onChange={e => setInterval(e.target.value)}
										className="w-full max-w-xs h-12 rounded-lg text-lg shadow-inner border"
									>
										<option value="3600">1 Hour</option>
										<option value="14400">4 Hours</option>
										<option value="86400">Daily</option>
										<option value="604800">Weekly</option>
									</select>
								</div>
							</div>
							<div className="flex justify-center">
								<button
									onClick={fetchBalanceHistory}
									className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-orange-400 hover:bg-orange-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
								>
									Load History
								</button>
							</div>
						</div>
					</div>
				)}

				{/* 以样式化方式显示地址数据和图表 */}
				{/* ... */}
				{balanceHistory && <BalanceChart historyData={balanceHistory} />}
			</main>
		</div>
	)
}

export default App

步骤 6:运行应用

现在,你可以通过运行以下命令来启动应用。

npm start

应用现在应该已经运行在 http://localhost:3000 上了。接下来,就可以与应用进行交互了。

你可以使用任意一个 Bitcoin 地址,或者使用示例中的 Bitcoin 地址(bc1qwfgdjyy95aay2686fn74h6a4nu9eev6np7q4fn204dkj3274frlqrskvx0)来测试应用。

Bitcoin Wallet Analytics App

总结

总而言之,通过构建实时 Bitcoin 钱包分析应用,你已经掌握了创建强大应用所需的知识和工具,这款应用能够为 Bitcoin 钱包交易和市场动态提供有价值的洞察。然而,你的应用的潜力并不仅限于此。有了基础结构,你完全有能力进一步扩展其功能。

可以考虑利用 Blockbook 中可用的其他方法,引入更多功能,例如跟踪多个地址、聚合钱包数据、支持更多法币,甚至构建一个完整的区块链浏览器。Blockbook 的灵活性允许集成更复杂的查询,并提供更深入的分析数据。

订阅我们的时事通讯,获取更多关于 Web3 和区块链的文章和指南。如果你有任何问题或需要进一步的帮助,欢迎加入我们的 Discord 服务器,或在页面末尾的反馈部分分享你的意见。通过关注我们的 Twitter(@Quicknode)和 Telegram 公告频道来了解最新动态。

  • 原文链接: quicknode.com/guides/qui...
  • 鸿途知科网 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
版权声明

本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门