链上资产
ONE SDK 的 engine client 提供了程序化 API,用于获取任意钱包地址在所有支持链上的链上余额和投资组合分析数据。
getWalletBalance
获取钱包地址的代币余额。可选择将查询范围限定到特定链。
import { useOneClient, CHAIN_IDS } from '@one_deploy/sdk';
function BalanceFetcher() {
const { engineClient } = useOneClient();
const fetchAllChains = async () => {
// 获取所有支持链上的余额
const balances = await engineClient.getWalletBalance(
'0x1234...abcd'
);
balances.forEach((b) => {
console.log(`${b.symbol} on chain ${b.chainId}: ${b.balance} ($${b.balanceUsd})`);
});
};
const fetchSpecificChains = async () => {
// 仅获取 Base 和 Ethereum 上的余额
const balances = await engineClient.getWalletBalance(
'0x1234...abcd',
[CHAIN_IDS.BASE, CHAIN_IDS.ETHEREUM]
);
console.log(`Found ${balances.length} token balances`);
};
return (
<div>
<button onClick={fetchAllChains}>All Chains</button>
<button onClick={fetchSpecificChains}>Base + Ethereum</button>
</div>
);
}
方法签名
getWalletBalance(
walletAddress: string,
chains?: number[]
): Promise<OnChainBalance[]>
参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
walletAddress | string | 是 | 要查询的钱包地址。 |
chains | number[] | 否 | 要查询的链 ID 数组。省略则查询所有支持的链。 |
OnChainBalance 类型
interface OnChainBalance {
/** 代币简称(如 'ETH'、'USDC')。 */
token: string;
/** 显示符号。 */
symbol: string;
/** 完整的代币名称。 */
name: string;
/** 格式化的余额,以十进制字符串表示。 */
balance: string;
/** 最小单位的原始余额(ETH 为 wei)。 */
balanceRaw: string;
/** 余额的美元等值。 */
balanceUsd: string;
/** 当前代币的美元价格。 */
priceUsd: string;
/** 24 小时价格变化百分比。 */
priceChange24h: string;
/** 余额所在的链 ID。 */
chainId: number;
/** 代币小数位数。 */
decimals: number;
/** ERC-20 合约地址,原生代币为 null。 */
contractAddress: string | null;
/** 代币 Logo URL。 */
logoUri: string;
/** 是否为原生 Gas 代币。 */
isNative: boolean;
}
getPortfolioSummary
获取钱包地址的聚合投资组合分析数据,包括总价值、配置分布和绩效指标。
import { useOneClient } from '@one_deploy/sdk';
function PortfolioView() {
const { engineClient } = useOneClient();
const loadPortfolio = async () => {
const portfolio = await engineClient.getPortfolioSummary(
'0x1234...abcd'
);
console.log('Total USD value:', portfolio.totalValueUsd);
console.log('24h change:', portfolio.change24h);
console.log('Chain breakdown:', portfolio.chainAllocation);
console.log('Top holdings:', portfolio.topHoldings);
};
return <button onClick={loadPortfolio}>Load Portfolio</button>;
}
方法签名
getPortfolioSummary(walletAddress: string): Promise<PortfolioAnalytics>
PortfolioAnalytics 类型
interface PortfolioAnalytics {
/** 查询的钱包地址。 */
walletAddress: string;
/** 投资组合总价值(美元)。 */
totalValueUsd: string;
/** 过去 24 小时的绝对美元变化。 */
change24h: string;
/** 过去 24 小时的百分比变化。 */
changePercent24h: string;
/** 按链的配置分布。 */
chainAllocation: ChainAllocation[];
/** 按美元价值降序排列的头部持仓。 */
topHoldings: HoldingSummary[];
/** 持有的不同代币数量。 */
tokenCount: number;
/** 有非零余额的链数量。 */
activeChainCount: number;
/** 数据计算时间的 ISO 8601 时间戳。 */
updatedAt: string;
}
interface ChainAllocation {
/** 链 ID。 */
chainId: number;
/** 链的显示名称。 */
chainName: string;
/** 该链上的总美元价值。 */
valueUsd: string;
/** 占总投资组合的百分比。 */
percentage: string;
}
interface HoldingSummary {
/** 代币符号。 */
symbol: string;
/** 代币名称。 */
name: string;
/** 格式化的余额。 */
balance: string;
/** 持仓的美元价值。 */
valueUsd: string;
/** 占总投资组合的百分比。 */
percentage: string;
/** 持仓所在的链 ID。 */
chainId: number;
}
获取和展示多链余额
一个完整的示例,展示如何从多条链获取余额并以分组视图渲染。
import { useState, useEffect } from 'react';
import { useOneClient, CHAIN_IDS } from '@one_deploy/sdk';
import type { OnChainBalance, PortfolioAnalytics } from '@one_deploy/sdk';
const CHAINS = [
{ id: CHAIN_IDS.BASE, name: 'Base' },
{ id: CHAIN_IDS.ETHEREUM, name: 'Ethereum' },
{ id: CHAIN_IDS.POLYGON, name: 'Polygon' },
{ id: CHAIN_IDS.ARBITRUM, name: 'Arbitrum' },
{ id: CHAIN_IDS.OPTIMISM, name: 'Optimism' },
];
function MultiChainPortfolio({ walletAddress }: { walletAddress: string }) {
const { engineClient } = useOneClient();
const [balances, setBalances] = useState<OnChainBalance[]>([]);
const [portfolio, setPortfolio] = useState<PortfolioAnalytics | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function load() {
setLoading(true);
const [balanceData, portfolioData] = await Promise.all([
engineClient.getWalletBalance(walletAddress),
engineClient.getPortfolioSummary(walletAddress),
]);
setBalances(balanceData);
setPortfolio(portfolioData);
setLoading(false);
}
load();
}, [walletAddress, engineClient]);
if (loading) return <p>Loading portfolio...</p>;
if (!portfolio) return <p>No data available.</p>;
// 按链分组余额
const balancesByChain = CHAINS.map((chain) => ({
...chain,
tokens: balances.filter((b) => b.chainId === chain.id),
})).filter((group) => group.tokens.length > 0);
return (
<div>
<header>
<h2>Portfolio: ${portfolio.totalValueUsd}</h2>
<p>
24h: {portfolio.change24h} ({portfolio.changePercent24h}%)
</p>
<p>
{portfolio.tokenCount} tokens across {portfolio.activeChainCount} chains
</p>
</header>
{balancesByChain.map((group) => (
<section key={group.id}>
<h3>{group.name}</h3>
<table>
<thead>
<tr>
<th>代币</th>
<th>余额</th>
<th>美元价值</th>
<th>价格</th>
<th>24h</th>
</tr>
</thead>
<tbody>
{group.tokens.map((token) => (
<tr key={`${token.chainId}-${token.contractAddress ?? 'native'}`}>
<td>{token.symbol}</td>
<td>{token.balance}</td>
<td>${token.balanceUsd}</td>
<td>${token.priceUsd}</td>
<td>{token.priceChange24h}%</td>
</tr>
))}
</tbody>
</table>
</section>
))}
</div>
);
}