如何构建实时比特币钱包分析应用程序

  • QuickNode
  • 发布于 2025-01-30 23:30
  • 阅读 28

本文提供了一个关于如何使用Blockbook RPC附加组件构建实时比特币钱包分析应用的全面指南,适合开发者和爱好者。文章详细介绍了所需的技术和工具,包括如何创建React应用、设置QuickNode Bitcoin端点、以及利用Blockbook的功能获取比特币地址的余额和交易历史,旨在帮助用户深入了解比特币钱包的活动与数据分析。

概述

在数字货币重塑金融交易的时代,跟踪和分析比特币交易的能力变得至关重要。本指南针对开发者和爱好者,解答了如何检查比特币余额历史或查找比特币地址余额的问题。它提供了使用Blockbook RPC Add-OnQuickNode Marketplace构建实时比特币钱包分析应用的详细步骤。

我们的应用旨在满足加密货币社区的一个普遍需求:不仅能够查看比特币钱包的当前余额,还能够探讨其历史交易和余额变化。这种实时分析工具对那些希望了解钱包活动的用户和开发者而言极具价值,提供了对趋势、交易模式以及比特币地址的整体金融历程的洞察。

无论你是希望增强个人项目,还是在专业应用中提供更多价值,或只是出于好奇想了解比特币钱包的内部 workings,本指南都将是你的起点。让我们开始这段旅程,解锁实时比特币钱包分析的潜力。

你将做什么

  • 学习如何使用BTC Blockbook JSON-RPC Add-On
  • 在QuickNode上创建比特币端点
  • 创建一个React应用,以获取比特币地址的交易数据,包括实时和历史余额

你所需的内容

  • 一个QuickNode账户(在此处免费注册)
  • 已安装Node.js
  • 具备JavaScript和React经验
  • 一个代码编辑器(如VScode
依赖 版本
node.js 最新

Blockbook能力概述

BTC Blockbook JSON-RPC Add-On作为一个强大的后端工具,旨在索引区块链交易,并提供全面的RPC接口以与像比特币这样的UTXO链进行交互。它的特点包括:

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

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

Blockbook对开发者的价值

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

Blockbook通过提供一个用户友好的API简化了这些挑战,抽象了直接与区块链交互的复杂性。它允许开发者查询钱包余额、交易历史和货币汇率,而不需要直接管理区块链数据,从而使开发者可以更多地集中于创建功能丰富的应用程序,而不必过多关注区块链数据管理的复杂性,显著加快了开发周期,减少了技术负担。

截至本文撰写时,Blockbook附加功能包含10种强大的RPC方法。本指南将利用其中三种:

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

设置比特币端点

使用Blockbook RPC设置比特币端点相当简单。如果你还没有注册账户,可以在此处创建一个账户。

登录后,导航到 quicknode.com/endpoints 页面,然后点击创建一个端点。选择比特币主网,然后点击下一步。之后,你将被提示配置附加功能。激活Blockbook RPC。然后,简单地点击创建端点

如果你已经有没有附加功能的比特币端点,请转到你的比特币端点内的附加功能页面,选择Blockbook RPC,并激活它。

Quicknode 端点页面

一旦你的端点准备就绪,复制_HTTP Provider_链接并保留,你将在下一节中使用它。

构建应用

要开始构建我们的比特币钱包分析应用,首先需要设置你的开发环境。如果尚未安装,请在计算机上安装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;

构建项目

设计应用程序结构

在开始编写代码之前,提前规划应用程序的结构通常是一个好习惯。对于本指南中构建的比特币钱包分析应用,你将需要AddressInputBalanceChartWalletStats等组件。

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

应用程序的可能操作流程如下:

  • 用户输入比特币地址并按下Enter/Return。
  • 使用bb_getAddress方法获取该地址的余额和交易数据。
  • 使用bb_getTickers方法获取比特币相对于不同法定货币的实时价格。
  • 地址数据和实时比特币价格数据将被传递给WalletStats组件。
  • 用户选择起始日期、结束日期和时间间隔,以获取该地址的历史BTC余额变化。
  • 使用bb_getBalanceHistory方法获取历史BTC余额数据。
  • 数据被传递到BalanceChart组件。

应用概览与使用方法

现在,让我们开始编码。

第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步:比特币地址输入组件

AddressInput组件包含一个用于输入比特币地址的表单。当表单提交时,它会触发作为prop传入的onAddressSubmit函数,并带上当前的地址状态。

该组件是一个自包含的表单,设计用于用户输入比特币地址,该地址可用于检索余额和交易数据。

打开src/components/AddressInput.js文件并按如下方式修改文件。

// src/components/AddressInput.js

import React, { useState } from 'react'

// 输入比特币地址的组件,提交表单时调用onAddressSubmit prop。
const AddressInput = ({ onAddressSubmit }) => {
    // 使用状态钩子存储和设置比特币地址
    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"
                    >
                        比特币地址的余额和图表
                    </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="请输入比特币地址"
                        />
                    </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"
                    >
                        提交
                    </button>
                </div>
            </form>
        </div>
    )
}

export default AddressInput

第2步:WalletStats组件

WalletStats组件显示各种比特币钱包统计数据,包括其余额与所选择法定货币的等值、总接收和发送BTC数量、总交易金额和未确认余额。它还允许用户切换显示最新的10个交易ID。

打开src/components/WalletStats.js文件并按如下方式修改文件。

// src/components/WalletStats.js

import React, { useState } from 'react'

// 将satoshi单位转换为比特币,修正到8位小数
const satoshiToBitcoin = satoshi => (satoshi / 100000000).toFixed(8)

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

    if (!walletData) {
        return <p>没有可用的数据。</p>
    }

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

    // 返回语句包含呈现钱包信息的JSX
    return (
        <div className="p-4 rounded-md shadow-md bg-white my-4">
            <h2 className="text-xl font-bold mb-4">钱包信息</h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                    <strong>地址:</strong> {walletData.address}
                </div>
                <div>
                    <strong>余额:</strong> {satoshiToBitcoin(walletData.balance)} BTC (
                    {fiatCurrency} {fiatBalance})
                </div>
                <div>
                    <strong>总接收:</strong>{' '}
                    {satoshiToBitcoin(walletData.totalReceived)} BTC
                </div>
                <div>
                    <strong>总发送:</strong> {satoshiToBitcoin(walletData.totalSent)}{' '}
                    BTC
                </div>
                <div>
                    <strong>总交易:</strong> {walletData.txs}
                </div>
                <div>
                    <strong>未确认余额:</strong>{' '}
                    {satoshiToBitcoin(walletData.unconfirmedBalance)} BTC
                </div>
                <div>
                    <strong>未确认交易:</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"
                    >
                        选择法定货币:
                    </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">美元</option>
                        <option value="EUR">欧元</option>
                        <option value="GBP">英镑</option>
                    </select>
                </div>
                {walletData.txids && walletData.txids.length > 0 && (
                    <div>
                        <button
                            onClick={() => setShowTxids(!showTxids)}
                            className="text-blue-600 hover:underline"
                        >
                            {showTxids ? '隐藏' : '显示'} 最新10个交易
                        </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组件渲染一个柱状图,以根据一组历史数据可视化比特币余额随时间的净变化。

打开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值转换为比特币并创建数据点
    const dataPoints = historyData.map(entry => ({
        t: new Date(entry.time * 1000),
        y:
            (parseInt(entry.received, 10) + // 解析接收的Satoshis字符串并转换为比特币
                parseInt(entry.sentToSelf, 10) - // 解析并添加发给自己的Satoshis,因为它们不会改变余额
                parseInt(entry.sent, 10)) / // 从钱包中解析并减去的Satoshis
            100000000, // Satoshi到比特币的转换系数
    }))

    // 按时间戳对数据点进行排序
    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: '随时间变化的净BTC余额变化',
                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: '日期',
                },
            },
            y: {
                beginAtZero: false,
                title: {
                    display: true,
                    text: '净BTC余额变化',
                },
            },
        },
        interaction: {
            intersect: false,
            mode: 'index',
        },
        responsive: true,
        maintainAspectRatio: true,
    }

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

export default BalanceChart

第4步:fetchData文件

fetchData文件提供了一组函数,用于使用Axios从比特币RPC API获取数据,以获取地址数据、检索比特币法定汇率和获取余额历史记录。

打开src/api/fetchData.js文件并按如下方式修改文件。请确保用你的QuickNode比特币端点替换YOUR_QUICKNODE_ENDPOINT占位符。

// src/api/fetchData.js

import axios from 'axios'

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

// 获取特定比特币地址数据的函数
export const getAddressData = async address => {
    const postData = {
        method: 'bb_getaddress', // 获取地址数据的RPC方法
        params: [address], // 方法的参数,此处为比特币地址
        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)
    }
}

// 获取特定时间戳的比特币法定汇率的函数
export const getBitcoinFiatRates = async timestampUnix => {
    const postData = {
        method: 'bb_gettickers', // 获取比特币法定汇率的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)
    }
}

// 获取比特币地址的余额历史的函数
export const getBalanceHistory = async (address, from, to, groupBy) => {
    const postData = {
        method: 'bb_getbalancehistory', // 获取余额历史的RPC方法
        params: [
            address, // 比特币地址
            {
                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 = () => {
    // 用于不同数据和用户界面控制的状态钩子
    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钩子在组件挂载时获取法定汇率
    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)
    }

    // 获取当前比特币法定汇率的函数
    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">比特币浏览器</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">
                                比特币余额图表
                            </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">
                                        起始日期
                                    </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">
                                        结束日期
                                    </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">
                                        时间间隔
                                    </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小时</option>
                                        <option value="14400">4小时</option>
                                        <option value="86400">每日</option>
                                        <option value="604800">每周</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"
                                >
                                    加载历史
                                </button>
                            </div>
                        </div>
                    </div>
                )}

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

export default App

第6步:运行应用

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

npm start

应用应在 http://localhost:3000 运行。现在,是时候与应用交互了。

你可以使用任何你想要的比特币地址或者在示例中使用的比特币地址( bc1qwfgdjyy95aay2686fn74h6a4nu9eev6np7q4fn204dkj3274frlqrskvx0)来测试应用。

比特币钱包分析应用

结论

总而言之,通过构建实时比特币钱包分析应用的旅程,你已经掌握了创建一个强大应用所需的知识和工具,该应用提供了对比特币钱包交易和市场动态的有价值见解。然而,你的应用潜力并不仅限于此。已经具备基础结构后,你可以进一步扩展其功能。

考虑利用Blockbook附加功能中可用的其他方法,引入跟踪多个地址、聚合钱包数据、支持更多法定货币或甚至构建完整区块链浏览器等附加功能。Blockbook的灵活性允许集成更复杂的查询和呈现更深层次的分析数据。

请订阅我们的电子报,获取更多有关Web3和区块链的文章和指南。如果你有任何问题或需要进一步的帮助,请随时加入我们的Discord服务器或使用下面的表单提供反馈。通过关注我们的Twitter(@QuickNode)和我们的Telegram公告频道保持最新。

我们❤️ 反馈!

让我们知道如果你有任何反馈或新的主题请求。我们很乐意听到你的声音。

  • 原文链接: quicknode.com/guides/qui...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。