参数
代理人的公钥(base-58 编码)
响应
包含以下内容的对象:
代币账户对象数组,包含:
代币账户的公钥(base-58 编码)
账户中的 lamports 数量
账户所有者的公钥(base-58 编码)
账户是否可执行
账户下次需要支付租金的 epoch
拥有该账户的程序(spl-token)
分配给账户的字节数
代码示例
基本请求
复制
curl https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenAccountsByDelegate",
"params": [
"4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj",
{
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
}
]
}'
使用 web3.js
复制
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');
// Get token accounts by delegate
const delegate = new PublicKey('4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj');
const programId = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
const accounts = await connection.getTokenAccountsByDelegate(delegate, { programId });
console.log('Token accounts:', accounts);
// Get token accounts by delegate with analysis
async function getTokenAccountsByDelegateWithAnalysis(
delegate: PublicKey,
config: { programId: PublicKey; commitment?: string }
) {
const accounts = await connection.getTokenAccountsByDelegate(delegate, config);
return {
accounts,
analysis: {
totalAccounts: accounts.value.length,
totalBalance: accounts.value.reduce((sum, acc) => {
const amount = BigInt(acc.account.data.parsed.info.tokenAmount.amount);
return sum + amount;
}, BigInt(0)),
byMint: accounts.value.reduce((acc, account) => {
const mint = account.account.data.parsed.info.mint;
const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount);
acc[mint] = (acc[mint] || BigInt(0)) + amount;
return acc;
}, {} as Record<string, bigint>),
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
注意事项
- 返回给定公钥作为代理人的所有代币账户
- 代理人有权从这些账户转移代币
- 响应是即时的,因为它从当前状态读取
- 账户可能随代币转账和其他操作而变化
- 代理人必须是有效的公钥
最佳实践
- 根据需求使用适当的 commitment 级别
- 在适当时缓存结果以减少 RPC 负载
- 监控代理账户的变化
- 考虑使用 WebSocket 订阅获取实时更新
- 适当处理网络错误并重试
常见错误
| 错误码 | 消息 | 解决方案 |
|---|---|---|
| -32601 | Method not found | 验证是否连接到 Solana RPC 节点 |
| -32602 | Invalid params | 检查代理人公钥和配置 |
| -32007 | Delegate not found | 验证代理人是否存在 |
| -32008 | Invalid program ID | 验证程序 ID 是否正确 |
用例
-
代理账户分析
复制
interface DelegateAccountAnalysis { delegate: string; accounts: Array<{ pubkey: string; mint: string; balance: string; owner: string; }>; metrics: { totalAccounts: number; totalBalance: string; byMint: Record<string, string>; byOwner: Record<string, number>; }; metadata: { timestamp: number; commitment?: string; }; } class DelegateAccountAnalyzer { async analyzeDelegateAccounts( delegate: PublicKey, config: { programId: PublicKey; commitment?: string } ): Promise<DelegateAccountAnalysis> { const accounts = await connection.getTokenAccountsByDelegate(delegate, config); const byMint: Record<string, bigint> = {}; const byOwner: Record<string, number> = {}; let totalBalance = BigInt(0); accounts.value.forEach(account => { const mint = account.account.data.parsed.info.mint; const owner = account.account.data.parsed.info.owner; const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount); byMint[mint] = (byMint[mint] || BigInt(0)) + amount; byOwner[owner] = (byOwner[owner] || 0) + 1; totalBalance += amount; }); return { delegate: delegate.toBase58(), accounts: accounts.value.map(account => ({ pubkey: account.pubkey, mint: account.account.data.parsed.info.mint, balance: account.account.data.parsed.info.tokenAmount.amount, owner: account.account.data.parsed.info.owner })), metrics: { totalAccounts: accounts.value.length, totalBalance: totalBalance.toString(), byMint: Object.fromEntries( Object.entries(byMint).map(([mint, amount]) => [mint, amount.toString()]) ), byOwner }, metadata: { timestamp: Date.now(), commitment: config.commitment } }; } } -
代理账户监控
复制
interface DelegateAccountChange { delegate: string; changes: Array<{ type: 'added' | 'removed' | 'modified'; account: string; previousBalance?: string; currentBalance?: string; }>; metadata: { timestamp: number; }; } class DelegateAccountMonitor { private previousAccounts: Map<string, string> = new Map(); async monitorDelegateAccounts( delegate: PublicKey, config: { programId: PublicKey; commitment?: string } ): Promise<DelegateAccountChange | null> { const accounts = await connection.getTokenAccountsByDelegate(delegate, config); const currentAccounts = new Map( accounts.value.map(account => [ account.pubkey, account.account.data.parsed.info.tokenAmount.amount ]) ); const changes: Array<{ type: 'added' | 'removed' | 'modified'; account: string; previousBalance?: string; currentBalance?: string; }> = []; this.previousAccounts.forEach((balance, account) => { if (!currentAccounts.has(account)) { changes.push({ type: 'removed', account, previousBalance: balance }); } else if (currentAccounts.get(account) !== balance) { changes.push({ type: 'modified', account, previousBalance: balance, currentBalance: currentAccounts.get(account) }); } }); currentAccounts.forEach((balance, account) => { if (!this.previousAccounts.has(account)) { changes.push({ type: 'added', account, currentBalance: balance }); } }); this.previousAccounts = currentAccounts; if (changes.length > 0) { return { delegate: delegate.toBase58(), changes, metadata: { timestamp: Date.now() } }; } return null; } } -
代理账户规划
复制
interface DelegateAccountPlan { delegate: string; currentAccounts: Array<{ pubkey: string; mint: string; balance: string; }>; recommendations: Array<{ type: 'consolidate' | 'diversify' | 'rebalance'; accounts: string[]; reason: string; }>; metadata: { timestamp: number; }; } class DelegateAccountPlanner { private readonly minBalancePerAccount = BigInt(1000000); // 1 token private readonly maxAccountsPerMint = 5; async planDelegateAccounts( delegate: PublicKey, config: { programId: PublicKey; commitment?: string } ): Promise<DelegateAccountPlan> { const accounts = await connection.getTokenAccountsByDelegate(delegate, config); const byMint = accounts.value.reduce((acc, account) => { const mint = account.account.data.parsed.info.mint; if (!acc[mint]) { acc[mint] = []; } acc[mint].push(account); return acc; }, {} as Record<string, typeof accounts.value>); const recommendations: Array<{ type: 'consolidate' | 'diversify' | 'rebalance'; accounts: string[]; reason: string; }> = []; Object.entries(byMint).forEach(([mint, mintAccounts]) => { const lowBalanceAccounts = mintAccounts.filter(account => BigInt(account.account.data.parsed.info.tokenAmount.amount) < this.minBalancePerAccount ); if (lowBalanceAccounts.length > 0) { recommendations.push({ type: 'consolidate', accounts: lowBalanceAccounts.map(account => account.pubkey), reason: `Accounts with balances below minimum threshold for mint ${mint}` }); } if (mintAccounts.length > this.maxAccountsPerMint) { recommendations.push({ type: 'consolidate', accounts: mintAccounts.map(account => account.pubkey), reason: `Too many accounts (${mintAccounts.length}) for mint ${mint}` }); } }); return { delegate: delegate.toBase58(), currentAccounts: accounts.value.map(account => ({ pubkey: account.pubkey, mint: account.account.data.parsed.info.mint, balance: account.account.data.parsed.info.tokenAmount.amount })), recommendations, metadata: { timestamp: Date.now() } }; } }