Перейти к основному содержанию

Параметры

delegate
string
обязательно
Открытый ключ делегата (в кодировке base-58)
config
object
обязательно
Объект конфигурации, содержащий:
programId
string
обязательно
Открытый ключ программы токенов (в кодировке base-58)
commitment
string
Уровень подтверждения (processed, confirmed, finalized)
encoding
string
Кодирование данных счёта (base58, base64, jsonParsed)
dataSlice
object
offset
number
Смещение в данных счёта
length
number
Длина возвращаемых данных

Ответ

result
object
Объект, содержащий:
context
object
slot
number
Слот, в котором был обработан запрос
value
array
Массив объектов токеновых счетов, содержащих:
pubkey
string
Открытый ключ токенового счёта (в кодировке base-58)
account
object
lamports
number
Количество lamports на счёте
owner
string
Открытый ключ владельца счёта (в кодировке base-58)
executable
boolean
Является ли счёт исполняемым
rentEpoch
number
Эпоха, в которую счёт должен будет оплатить аренду следующий раз
data
object
parsed
object
info
object
tokenAmount
object
amount
string
Необработанное количество токенов в виде строки
decimals
number
Количество десятичных знаков, настроенных для монетного двора токена
uiAmount
number
Количество токенов в виде числа с плавающей точкой с учётом десятичных знаков
uiAmountString
string
Количество токенов в виде строки с учётом десятичных знаков
mint
string
Открытый ключ монетного двора токена (в кодировке base-58)
owner
string
Открытый ключ владельца токенового счёта (в кодировке base-58)
delegate
string
Открытый ключ делегата (в кодировке base-58)
type
string
Тип счёта (account)
program
string
Программа, владеющая счётом (spl-token)
space
number
Количество байтов, выделенных для счёта

Примеры кода

Базовый запрос

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
      }
    }
  };
}

Примечания

  1. Возвращает все токеновые счета, для которых указанный открытый ключ является делегатом
  2. Делегат имеет право переводить токены с этих счетов
  3. Ответ приходит немедленно, так как читается из текущего состояния
  4. Счета могут изменяться при переводах токенов и других операциях
  5. Делегат должен быть действительным открытым ключом

Рекомендации

  1. Используйте подходящий уровень подтверждения в зависимости от ваших потребностей
  2. Кэшируйте результаты там, где это уместно, для снижения нагрузки на RPC
  3. Отслеживайте изменения в делегированных счетах
  4. Рассмотрите использование подписки на websocket для обновлений в реальном времени
  5. Обрабатывайте сетевые ошибки и выполняйте повторные попытки при необходимости

Распространённые ошибки

КодСообщениеРешение
-32601Method not foundУбедитесь, что вы подключены к узлу Solana RPC
-32602Invalid paramsПроверьте открытый ключ делегата и конфигурацию
-32007Delegate not foundУбедитесь, что делегат существует
-32008Invalid program IDПроверьте корректность ID программы

Сценарии использования

  1. Анализ счетов делегата
    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
          }
        };
      }
    }
    
  2. Мониторинг счетов делегата
    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;
      }
    }
    
  3. Планирование счетов делегата
    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()
          }
        };
      }
    }