Параметры
Открытый ключ делегата (в кодировке base-58)
Объект конфигурации, содержащий:Открытый ключ программы токенов (в кодировке base-58)
Уровень подтверждения (processed, confirmed, finalized)
Кодирование данных счёта (base58, base64, jsonParsed)
Длина возвращаемых данных
Ответ
Объект, содержащий:Слот, в котором был обработан запрос
Массив объектов токеновых счетов, содержащих:Открытый ключ токенового счёта (в кодировке base-58)
Количество lamports на счёте
Открытый ключ владельца счёта (в кодировке base-58)
Является ли счёт исполняемым
Эпоха, в которую счёт должен будет оплатить аренду следующий раз
Необработанное количество токенов в виде строки
Количество десятичных знаков, настроенных для монетного двора токена
Количество токенов в виде числа с плавающей точкой с учётом десятичных знаков
Количество токенов в виде строки с учётом десятичных знаков
Открытый ключ монетного двора токена (в кодировке base-58)
Открытый ключ владельца токенового счёта (в кодировке base-58)
Открытый ключ делегата (в кодировке base-58)
Программа, владеющая счётом (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
}
}
};
}
Примечания
- Возвращает все токеновые счета, для которых указанный открытый ключ является делегатом
- Делегат имеет право переводить токены с этих счетов
- Ответ приходит немедленно, так как читается из текущего состояния
- Счета могут изменяться при переводах токенов и других операциях
- Делегат должен быть действительным открытым ключом
Рекомендации
- Используйте подходящий уровень подтверждения в зависимости от ваших потребностей
- Кэшируйте результаты там, где это уместно, для снижения нагрузки на 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;
}> = [];
// Check for removed or modified accounts
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)
});
}
});
// Check for added accounts
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]) => {
// Check for accounts with low balances
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}`
});
}
// Check for too many accounts per 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()
}
};
}
}