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

本文介绍了如何使用 QuickNode Marketplace 上的 Blockbook RPC Add-On 构建一个实时比特币钱包分析应用APP,该应用可以帮助用户查看比特币钱包的当前余额,并探索其历史交易和余额变化。文章详细介绍了Blockbook的各项能力,以及如何在QuickNode上设置比特币节点,并搭建React应用来获取比特币地址的交易数据,包括实时和历史余额。

概述

在数字货币重塑金融交易的时代,追踪和分析比特币交易的能力变得至关重要。本综合指南专为经常询问如何查看比特币余额历史记录或查找比特币地址余额的开发者和爱好者量身定制。它提供了使用来自 QuickNode MarketplaceBlockbook RPC Add-On 构建实时比特币钱包分析应用程序的详细演练。

我们的应用程序解决了加密货币社区中的一个常见需求:不仅能够查看比特币钱包的当前余额,还能探索其历史交易和余额变化。这种实时分析工具对于想要了解一段时间内的钱包活动、提供对趋势、交易模式以及比特币地址的整体财务历程的见解的用户和开发者来说都非常宝贵。

无论你是希望增强个人项目、在专业应用程序中提供更多价值,还是仅仅对复杂的比特币钱包运作方式感到好奇,本指南都将作为你的起点。让我们开始这段旅程,释放实时比特币钱包分析的潜力。

你将做什么

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

你需要的

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

Blockbook 功能概述

BTC Blockbook JSON-RPC Add-On 作为一个强大的后端工具,旨在索引区块链交易并提供一个全面的 RPC 接口来与 UTXO 链(如 Bitcoin)进行交互。 它以以下特点脱颖而出:

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

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

Blockbook 对开发者的价值

如果没有 Blockbook,开发者将不得不直接与比特币区块链进行交互才能获得此信息。此过程涉及设置和维护一个完整节点,解析原始区块链数据,并不断与最新的网络交易同步。这些任务不仅资源密集,而且还需要对区块链协议和数据结构的深入理解。

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

截至撰写本文时,Blockbook 插件包括 10 种强大的 RPC 方法。我们将在本指南中利用其中的三种:

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

设置比特币端点

使用 Blockbook RPC 设置比特币端点非常容易。如果你尚未注册,可以在 here 创建一个帐户。

登录后,导航到 quicknode.com/endpoints 页面,然后单击 创建端点。 选择 Bitcoin mainnet,然后单击下一步。 然后,系统将提示你配置插件。 激活 Blockbook RPC。 之后,只需单击 创建端点 即可。

如果你已经有一个没有插件的比特币端点,请转到你的比特币端点中的 插件 页面,选择 Blockbook RPC,然后激活它。

Quicknode Endpoints page

端点准备就绪后,复制 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 组件。

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

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

此组件是一个独立的表单,旨在供用户输入比特币地址,然后可以使用该地址检索余额和交易数据。

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

// src/components/AddressInput.js

import React, { useState } from 'react'

// 用于输入比特币地址的组件,它在表单提交时调用 onAddressSubmit prop。
const AddressInput = ({ onAddressSubmit }) => {
    // 用于存储和设置比特币地址的状态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"
                    >
                        比特币地址的余额和图表
                    </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 单位转换为 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>没有可用数据。</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">钱包信息</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">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 ? '隐藏' : '显示'} 最新 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 值转换为 Bitcoin 并创建数据点
    const dataPoints = historyData.map(entry => ({
        t: new Date(entry.time * 1000),
        y:
            (parseInt(entry.received, 10) + // 解析接收到的聪字符串并转换为比特币
                parseInt(entry.sentToSelf, 10) - // 解析和添加发送给自己的聪,因为它们不会改变余额
                parseInt(entry.sent, 10)) / // 解析并从钱包中减去发送的聪
            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: '随时间变化的净 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 从 Bitcoin RPC API 获取数据,用于不同的目的:获取地址数据、检索比特币法定汇率以及获取余额历史记录。

用你的代码编辑器打开 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'

// 用于获取特定比特币地址数据的函数
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 = () => {
    // 各种数据和 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)
    }

    // 用于获取当前比特币法定汇率的函数
    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">
                                比特币余额图表
                            </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_) 来测试该应用程序。

Bitcoin Wallet Analytics App

结论

总之,通过构建实时比特币钱包分析应用程序的旅程,你已经掌握了知识和工具来创建一个强大的应用程序,该应用程序可以提供对比特币钱包交易和市场动态的宝贵见解。 但是,你的应用程序的潜力并不止于此。 凭借已有的基础结构,你可以很好地扩展其功能。

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

订阅我们的 newsletter 以获取有关 Web3 和区块链的更多文章和指南。 如果你有任何问题或需要更多帮助,请随时加入我们的 Discord 服务器或使用下面的表格提供反馈。 通过在 Twitter (@QuickNode) 和我们的 Telegram announcement channel 上关注我们,及时了解最新信息。

我们 ❤️ 反馈!

如果你对新主题有任何反馈或要求,请 Let us know。 我们很乐意听取你的意见。

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

0 条评论

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