本文介绍了如何使用 QuickNode 的 Hyperliquid info endpoint 构建一个 Hyperliquid 投资组合跟踪器,该跟踪器可以实时监控用户的仓位、盈亏和保证金使用情况。文章详细阐述了如何设置 QuickNode endpoint 和 Supabase 账户,搭建数据库 schema,创建索引器,以及构建一个可以展示实时交易数据的仪表盘。
作为 Hyperliquid 上的永续交易者,拥有一个全面的投资组合跟踪器对于实时监控你的仓位、盈亏和保证金利用率至关重要。本指南将向你展示如何使用 QuickNode 的 Hyperliquid info endpoint 构建一个强大的投资组合跟踪器,该跟踪器可以监控任何 Hyperliquid 钱包。除了创建一个有用的交易工具之外,本教程还将揭秘如何获取、构建和利用 HyperCore 数据来构建应用程序。该应用程序展示了:
分 4 个阶段构建一个完整的投资组合跟踪器:
为什么选择 QuickNode Endpoint?
QuickNode 提供专用的 Hyperliquid API endpoint,无需运行你自己的节点:
投资组合跟踪器由三个组件组成,这些组件通过 PostgreSQL 数据库进行通信。 Indexer 从 Hyperliquid 获取数据,将其存储在数据库中,前端查询数据库以进行显示。
轮询注意事项
本指南使用较短的轮询间隔(Indexer 为 500 毫秒,前端为 1000 毫秒)来演示实时更新。你可以根据需要调整这些间隔:
src/Dashboard.tsx
第 260-264 行 - 在以下代码中更改 1000
值:const interval = setInterval(async () => {
await fetchData(currentWallet);
}, 1000);
src/indexer/indexer.ts
第 623-630 行 - 在以下代码中更改 500
值:setInterval(async () => {
await indexer.checkForWalletSwitch();
await indexer.indexData();
}, 500);
监控你的 QuickNode 和 Supabase 使用情况以优化成本。
DECIMAL
类型)存储交易数据wallet_switch_requests
表处理前端和 indexer 之间的通信info
endpoint
┌─────────────────┐
│ Perp 交易者 │
└─────────┬───────┘
│ 1. 输入钱包地址
▼
┌─────────────────┐
│ React 仪表盘 │◄─────────────────┐
└─────────┬───────┘ │
│ 2. 存储请求 │ 6. 读取 & 显示数据
▼ │
┌─────────────────┐ │
│ Supabase │◄─────────────────┤
│ PostgreSQL │ │
└─────────┬───────┘ │
│ 3. 检测到请求 │ 5. 存储数据
▼ │
┌─────────────────┐ │
│ Indexer │──────────────────┘
│ (500ms 轮询) │
└─────────┬───────┘
│ 4. 获取 HyperCore 数据
▼
┌─────────────────┐
│ QuickNode │
│ Hyperliquid │
│ Endpoint │
└─────────────────┘
第一次钱包搜索:
第二次及以后的钱包搜索:
该项目遵循一个清晰的、模块化的架构,该架构分离了数据收集、UI 组件和业务逻辑之间的关注点。这种结构使代码库可维护,并允许轻松扩展功能。
├── src/
│ ├── indexer/
│ │ ├── indexer.ts # 主要的 indexer 编排和钱包管理
│ │ └── apicalls.ts # Hyperliquid info endpoint 查询
│ ├── components/
│ │ ├── ui/ # shadcn/ui 组件(Button, Input, Card, 等)
│ │ └── dashboard/ # 仪表盘组件(WalletHeader, PortfolioMetrics, 等)
│ ├── shared/
│ │ ├── types.ts # TypeScript 接口 & 类型
│ │ ├── utils.ts # 格式化、计算 & 实用函数
│ │ ├── constants.ts # 仪表盘的 UI 常量
│ │ └── supabase.ts # 前端访问的 Supabase 客户端实例
│ ├── Dashboard.tsx # 主要的仪表盘逻辑
│ └── main.tsx
├── supabase/
│ └── schema.sql # 完整的数据库 schema
├── package.json
└── .env
首先,克隆项目存储库并导航到项目目录:
git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/sample-dapps/hyperliquid-portfolio-tracker
通过运行以下命令创建你的 .env
文件:
cp .env.example .env
在 Supabase 网站 上创建一个新的 Supabase 账户或登录到你现有的 Supabase 账户。
创建一个新项目,然后单击 Connect 按钮。
在 App Frameworks 部分,选择 React 并将 using
字段更改为 Vite。复制 VITE_SUPABASE_URL
和 VITE_SUPABASE_ANON_KEY
的值并将它们添加到你的 .env
文件中。
最后,导航到右上角的 SQL Editor,粘贴 schema.sql
文件的内容,然后单击 run。这将创建所有必要的表和函数,我们需要存储 & 获取前端的数据。
创建你的免费试用 QuickNode 账户,然后创建你的第一个 Hyperliquid RPC endpoint 并将其粘贴到你的 .env
文件中。
info
确保删除现有的 /evm
并在你的 QuickNode endpoint URL 的末尾添加 /info
,以获取对 Hyperliquid info endpoint 的访问权限。
创建表并配置环境后,你可以通过在根目录中运行以下命令来启动项目:
npm install && npm run dev:both
这将在运行前端应用程序的同时运行 indexer。
当 indexer 开始运行时,它将等待你搜索有效的钱包地址:
在 localhost URL 上打开你的前端页面,然后单击演示钱包按钮以获取示例钱包地址:
搜索钱包后,indexer 将开始每 500 毫秒获取该地址的数据:
仪表盘将显示钱包的实时统计信息:
你现在可以实时访问账户价值、活跃仓位和其他交易数据。
在使用 indexer 时,你可能会在设置或运行时遇到以下问题:
解决方案:
通过运行以下命令重新启动 indexer:
npm run dev:indexer
然后尝试通过输入有效的钱包地址再次搜索。
现在你已经启动并运行了投资组合跟踪器,让我们探索一下它在幕后是如何工作的。本节将深入探讨三个核心组件,这些组件使实时投资组合跟踪成为可能:
Indexer - 通过 apicalls.ts
处理 Hyperliquid 的 info
endpoint,检测钱包切换请求,并每 500 毫秒编排从 5 个不同的 Hyperliquid endpoint 收集数据。
Schema & DB - 演示了 PostgreSQL 表结构,这些结构以财务精度存储交易数据,包括前端-indexer 通信的协调机制。
仪表盘(前端) - 涵盖 React 状态管理、实时数据库轮询、钱包地址验证以及显示实时交易数据的用户界面。
Indexer 作为一个单独的 Node.js 进程运行,每 500 毫秒轮询 QuickNode endpoint。它获取当前钱包地址的交易数据,并将其存储在数据库中。 Indexer 等待来自前端的钱包切换请求,并使用锁文件处理进程隔离。
Indexer 每 500 毫秒轮询 wallet_switch_requests
表,以检测前端钱包切换,使用状态字段来防止切换过程中的竞争条件。
info
Endpoint 集成Indexer 使用 apicalls.ts
中的 HyperliquidAPI
类与 QuickNode 的 Hyperliquid endpoint 通信。每个 API 方法都遵循一致的模式来获取不同类型的交易数据:
// 来自 apicalls.ts - 主要账户数据获取
async getClearinghouseState(walletAddress: string): Promise<ClearinghouseStateResponse> {
const payload = {
type: 'clearinghouseState',
user: walletAddress
};
const response = await fetch(QUICKNODE_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
return await response.json();
}
getUserVaultEquities()
方法遵循相同的结构,但使用 type: 'userVaultEquities'
,这表明每个 endpoint 仅需要钱包地址和 endpoint 类型。所有方法都包括全面的错误处理和日志记录,以调试交易数据问题。
主要的索引循环调用 5 个不同的 info
endpoint,并将结果存储在单独的数据库表中:
// 来自 indexer.ts - 核心数据获取和存储
const data = await hyperliquidAPI.getClearinghouseState(CURRENT_WALLET_ADDRESS);
const stateId = await this.storeClearinghouseState(data);
// 以原子替换方式存储仓位,以防止 UI 闪烁
await this.storeAssetPositions(data.assetPositions, data.time);
// 获取其他数据类型,并进行错误处理
try {
const rateLimitData = await hyperliquidAPI.getUserRateLimit(CURRENT_WALLET_ADDRESS);
await this.storeUserRateLimit(rateLimitData, timestamp);
} catch (error) {
console.log(`No rate limit data available for ${CURRENT_WALLET_ADDRESS}`);
}
Indexer 可以优雅地处理单个 endpoint 故障 - 如果一个数据源失败,其他数据源将继续正常工作。
Indexer 使用基于文件的锁来防止多个实例同时运行:
// 来自 indexer.ts - 锁文件创建和进程检查
function createLock(): boolean {
if (fs.existsSync(LOCK_FILE)) {
const { pid } = JSON.parse(fs.readFileSync(LOCK_FILE, 'utf8'));
try {
process.kill(pid, 0);
console.error(`❌ 另一个 indexer 已经在运行 (PID: ${pid})`);
return false;
} catch (e) {
fs.unlinkSync(LOCK_FILE); // 删除过时的锁
}
}
fs.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid }));
return true;
}
Indexer 使用锁文件来防止多个实例同时运行,从而确保数据一致性。
现在我们已经了解了 indexer 如何收集数据,让我们检查一下如何存储和构造这些数据。 PostgreSQL 数据库在 6 个表中存储交易数据,并处理前端和 indexer 之间的通信。每个表都使用 DECIMAL
类型表示财务精度,并使用唯一约束来防止重复条目。
资产仓位表** - 存储具有财务精度的永续交易仓位:
-- 来自 schema.sql - 核心交易数据存储
CREATE TABLE asset_positions (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
wallet_address TEXT NOT NULL,
coin TEXT NOT NULL, -- 资产符号(例如,“BTC”、“ETH”、“SOL”)
size DECIMAL(20, 5) NOT NULL, -- 仓位大小:总共 20 位数字,小数点后 5 位
leverage_type TEXT NOT NULL, -- “cross”或“isolated”保证金模式
leverage_value INTEGER NOT NULL, -- 杠杆倍数(1 倍、5 倍、10 倍等)
entry_price DECIMAL(20, 5), -- 平均入场价格,精度为小数点后 5 位
position_value DECIMAL(20, 5), -- 仓位当前的美元价值
unrealized_pnl DECIMAL(20, 5), -- 平仓前的盈/亏
liquidation_price DECIMAL(20, 5), -- 仓位被清算的价格
margin_used DECIMAL(20, 5), -- 分配给该仓位的保证金金额
timestamp BIGINT NOT NULL, -- 来自 HyperCore 的毫秒级 Unix 时间戳
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -- 数据库插入时间
);
-- 唯一约束可防止每个钱包-币种对的重复仓位
ALTER TABLE asset_positions ADD CONSTRAINT unique_position_per_wallet
UNIQUE (wallet_address, coin);
DECIMAL(20, 5)
类型提供总共 20 位数字,小数点后 5 位。
钱包切换请求表 - 协调前端和 indexer 之间的通信:
-- 来自 schema.sql - 前端-indexer 协调
CREATE TABLE wallet_switch_requests (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
requested_wallet_address TEXT NOT NULL, -- Ethereum 地址(0x 格式)
status TEXT NOT NULL DEFAULT 'pending', -- 状态机:pending → processing → completed
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 用于 indexer 基于状态的高效查询的索引
CREATE INDEX idx_wallet_switch_requests_status ON wallet_switch_requests(status);
此表充当 React 前端和 Node.js indexer 之间的消息队列。当用户输入新的钱包地址时,前端会插入一个 'pending'
请求。 Indexer 每 500 毫秒轮询一次 'pending'
请求,将状态更新为 'processing'
以防止竞争条件,然后执行钱包切换。状态进展确保一次只发生一个钱包切换,即使使用者快速搜索不同的地址也是如此。
Indexer 收集数据并将数据存储在数据库中后,最后一部分就是用户界面。 React 前端每 1000 毫秒轮询一次数据库,以获取更新的交易数据,并使用模块化组件显示该数据。它通过将请求插入数据库并立即清除本地状态来管理钱包切换。
仪表盘架构围绕着中心化状态管理和模块化组件组合:
// 来自 Dashboard.tsx - 中心化状态管理
const [latestState, setLatestState] = useState<ClearinghouseState | null>(null);
const [positions, setPositions] = useState<AssetPosition[]>([]);
const [vaultEquities, setVaultEquities] = useState<UserVaultEquity[]>([]);
const [spotBalances, setSpotBalances] = useState<SpotBalance[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [hasInitialData, setHasInitialData] = useState(false);
Dashboard
组件将所有交易数据保存在 React 状态中,并将其作为 props 传递给子组件。这种模式使状态管理变得简单,并可以轻松跟踪调试期间的数据流。
前端使用带有适当清理的 setInterval
循环每 1000 毫秒轮询一次数据库:
// 来自 Dashboard.tsx - 使用清理自动刷新
useEffect(() => {
if (currentWallet && hasStarted && hasInitialData) {
const interval = setInterval(async () => {
await fetchData(currentWallet);
}, 1000);
return () => clearInterval(interval);
}
}, [currentWallet, hasStarted, hasInitialData, fetchData]);
// 数据新鲜度检测提供视觉反馈
const isDataStale = latestState && (Date.now() - latestState.timestamp > 3000);
轮询会根据用户操作自动启动和停止,仅在需要时运行以节省资源。 界面显示数据何时过时(超过 3 秒),以使用户了解数据的新鲜度。
前端使用优化的查询模式从 Supabase 实时获取交易数据:
// 来自 Dashboard.tsx - 实时数据查询
const { data: latestData, error: latestError } = await supabase
.from('clearinghouse_states')
.select('*')
.eq('wallet_address', walletAddress)
.order('timestamp', { ascending: false })
.limit(1)
.maybeSingle();
// 获取此钱包的所有仓位
const { data: positionsData, error: positionsError } = await supabase
.from('asset_positions')
.select('*')
.eq('wallet_address', walletAddress)
.order('timestamp', { ascending: false });
if (positionsError && positionsError.code !== 'PGRST116') throw positionsError;
这些查询可以优雅地处理钱包没有交易数据的情况,显示空结果而不是错误。
前端验证钱包地址,并在切换时立即清除状态:
// 来自 Dashboard.tsx - 钱包验证和切换
const isValidWalletAddress = (address: string): boolean => {
return /^0x[a-fA-F0-9]{40}$/.test(address);
};
const handleWalletSearch = async () => {
if (!isValidWalletAddress(address)) {
setError('Invalid wallet address format');
return;
}
// 切换钱包时立即清除所有旧数据
setLatestState(null);
setPositions([]);
setVaultEquities([]);
setSpotBalances([]);
setIsSearching(true);
// 向 indexer 发出切换信号
await switchIndexerWallet(address);
setCurrentWallet(address);
};
界面验证钱包地址,并在切换时立即清除旧数据,显示加载状态,直到加载新数据为止。
交易数据从 React 状态流向专用 UI 组件,这些组件格式化并呈现信息:
// 来自 PortfolioMetrics.tsx - 投资组合概览卡片组件
interface PortfolioMetricsProps {
totalAccountValue: number;
totalUnrealizedPnl: number;
userRateLimit: UserRateLimit | null;
vaultEquities: UserVaultEquity[];
delegations: Delegation[];
formatCurrency: (value: number) => string;
}
export const PortfolioMetrics: React.FC<PortfolioMetricsProps> = ({
totalAccountValue,
totalUnrealizedPnl,
formatCurrency
}) => {
return (
<Card className="bg-slate-900/50 border-slate-700/50 backdrop-blur-sm mb-6">
<CardContent className="p-4">
<div className="text-xs text-slate-400 mb-3 font-medium tracking-wide uppercase">
Perp 账户价值
</div>
<div className="text-2xl font-bold text-white">
{formatCurrency(totalAccountValue)}
</div>
</CardContent>
</Card>
);
};
Dashboard
组件将计算出的值和格式化函数传递给子组件,这些子组件处理交易数据的视觉呈现和样式设置。
恭喜! 你已成功使用 QuickNode 的 Hyperliquid info endpoint 构建了实时 Hyperliquid 投资组合跟踪器。 你已经学习了如何获取永续交易数据、构建具有财务精度的 PostgreSQL 数据库以及构建实时更新的响应式仪表盘。 这种基础解锁了高级交易工具的可能性,例如自动风险监控和多钱包比较。
你可以通过集成 Recharts 等图表库或使用你喜欢的通知服务构建交易警报来进一步扩展此项目。 查看我们的其他 Hyperliquid 指南,以探索更多在 Hyperliquid 上构建的方法。
现在你有了一个可以正常工作的投资组合跟踪器,以下是扩展和改进应用程序的几种方法:
如果你遇到困难或有疑问,请将其放入我们的 Discord。 通过在 Twitter (@QuickNode) 或我们的 Telegram 公告频道 上关注我们来了解最新信息。
如果你对新主题有任何反馈或请求,请告诉我们。 我们很乐意听取你的意见。
- 原文链接: quicknode.com/guides/oth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!