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

Параметры

mint
string
обязательно
Открытый ключ монетного двора токена (в кодировке base-58)
config
object
Объект конфигурации, содержащий:
limit
number
Максимальное количество возвращаемых счетов (по умолчанию: 20)
commitment
string
Уровень подтверждения (processed, confirmed, finalized)

Ответ

result
object
Объект, содержащий:
context
object
slot
number
Слот, в котором был обработан запрос
value
array
Массив объектов токеновых счетов, содержащих:
address
string
Открытый ключ токенового счёта (в кодировке base-58)
amount
string
Необработанное количество токенов в виде строки
decimals
number
Количество десятичных знаков, настроенных для монетного двора токена
uiAmount
number
Количество токенов в виде числа с плавающей точкой с учётом десятичных знаков
uiAmountString
string
Количество токенов в виде строки с учётом десятичных знаков

Примеры кода

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

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": "getTokenLargestAccounts",
  "params": [
    "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    {
      "limit": 10
    }
  ]
}'

Использование web3.js

import { Connection, PublicKey } from '@solana/web3.js';

const connection = new Connection('https://fra.rpc.orbitflare.com?api_key=YOUR-API-KEY');

// Get largest token accounts
const mint = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
const accounts = await connection.getTokenLargestAccounts(mint, { limit: 10 });
console.log('Largest token accounts:', accounts);

// Get largest token accounts with analysis
async function getTokenLargestAccountsWithAnalysis(
  mint: PublicKey,
  config: { limit?: number; commitment?: string }
) {
  const accounts = await connection.getTokenLargestAccounts(mint, config);
  
  return {
    accounts,
    analysis: {
      totalAccounts: accounts.value.length,
      totalSupply: accounts.value.reduce((sum, acc) => {
        const amount = BigInt(acc.amount);
        return sum + amount;
      }, BigInt(0)),
      distribution: accounts.value.reduce((acc, account, index) => {
        const amount = BigInt(account.amount);
        const percentage = Number((amount * BigInt(100)) / BigInt(accounts.value[0].amount));
        acc[account.address] = {
          amount: account.amount,
          percentage,
          rank: index + 1
        };
        return acc;
      }, {} as Record<string, {
        amount: string;
        percentage: number;
        rank: number;
      }>),
      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Проверьте открытый ключ монетного двора и конфигурацию
-32007Mint not foundУбедитесь, что монетный двор существует
-32008Invalid limitУбедитесь, что лимит является положительным числом

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

  1. Анализ распределения токенов
    interface TokenDistributionAnalysis {
      mint: string;
      accounts: Array<{
        address: string;
        amount: string;
        percentage: number;
        rank: number;
      }>;
      metrics: {
        totalAccounts: number;
        totalSupply: string;
        concentration: number; // Gini coefficient
        top10Percentage: number;
      };
      metadata: {
        timestamp: number;
        commitment?: string;
      };
    }
    
    class TokenDistributionAnalyzer {
      private calculateGiniCoefficient(amounts: bigint[]): number {
        const n = amounts.length;
        const mean = amounts.reduce((sum, x) => sum + x, BigInt(0)) / BigInt(n);
        let numerator = BigInt(0);
        let denominator = BigInt(0);
        
        for (let i = 0; i < n; i++) {
          for (let j = 0; j < n; j++) {
            numerator += (amounts[i] - amounts[j]).abs();
          }
          denominator += amounts[i];
        }
        
        return Number(numerator) / (2 * n * Number(denominator));
      }
      
      async analyzeTokenDistribution(
        mint: PublicKey,
        config: { limit?: number; commitment?: string }
      ): Promise<TokenDistributionAnalysis> {
        const accounts = await connection.getTokenLargestAccounts(mint, config);
        const amounts = accounts.value.map(acc => BigInt(acc.amount));
        const totalSupply = amounts.reduce((sum, x) => sum + x, BigInt(0));
        const top10Supply = amounts.slice(0, 10).reduce((sum, x) => sum + x, BigInt(0));
        
        return {
          mint: mint.toBase58(),
          accounts: accounts.value.map((account, index) => ({
            address: account.address,
            amount: account.amount,
            percentage: Number((BigInt(account.amount) * BigInt(100)) / totalSupply),
            rank: index + 1
          })),
          metrics: {
            totalAccounts: accounts.value.length,
            totalSupply: totalSupply.toString(),
            concentration: this.calculateGiniCoefficient(amounts),
            top10Percentage: Number((top10Supply * BigInt(100)) / totalSupply)
          },
          metadata: {
            timestamp: Date.now(),
            commitment: config.commitment
          }
        };
      }
    }
    
  2. Мониторинг крупных держателей токенов
    interface TokenHolderChange {
      mint: string;
      changes: Array<{
        type: 'added' | 'removed' | 'modified';
        address: string;
        previousAmount?: string;
        currentAmount?: string;
        previousRank?: number;
        currentRank?: number;
      }>;
      metadata: {
        timestamp: number;
      };
    }
    
    class TokenHolderMonitor {
      private previousHolders: Map<string, {
        amount: string;
        rank: number;
      }> = new Map();
      
      async monitorTokenHolders(
        mint: PublicKey,
        config: { limit?: number; commitment?: string }
      ): Promise<TokenHolderChange | null> {
        const accounts = await connection.getTokenLargestAccounts(mint, config);
        const currentHolders = new Map(
          accounts.value.map((account, index) => [
            account.address,
            {
              amount: account.amount,
              rank: index + 1
            }
          ])
        );
        
        const changes: Array<{
          type: 'added' | 'removed' | 'modified';
          address: string;
          previousAmount?: string;
          currentAmount?: string;
          previousRank?: number;
          currentRank?: number;
        }> = [];
        
        // Check for removed or modified holders
        this.previousHolders.forEach((data, address) => {
          if (!currentHolders.has(address)) {
            changes.push({
              type: 'removed',
              address,
              previousAmount: data.amount,
              previousRank: data.rank
            });
          } else {
            const current = currentHolders.get(address)!;
            if (current.amount !== data.amount || current.rank !== data.rank) {
              changes.push({
                type: 'modified',
                address,
                previousAmount: data.amount,
                currentAmount: current.amount,
                previousRank: data.rank,
                currentRank: current.rank
              });
            }
          }
        });
        
        // Check for added holders
        currentHolders.forEach((data, address) => {
          if (!this.previousHolders.has(address)) {
            changes.push({
              type: 'added',
              address,
              currentAmount: data.amount,
              currentRank: data.rank
            });
          }
        });
        
        this.previousHolders = currentHolders;
        
        if (changes.length > 0) {
          return {
            mint: mint.toBase58(),
            changes,
            metadata: {
              timestamp: Date.now()
            }
          };
        }
        
        return null;
      }
    }
    
  3. Планирование предложения токенов
    interface TokenSupplyPlan {
      mint: string;
      currentHolders: Array<{
        address: string;
        amount: string;
        rank: number;
      }>;
      recommendations: Array<{
        type: 'diversify' | 'consolidate' | 'rebalance';
        addresses: string[];
        reason: string;
      }>;
      metadata: {
        timestamp: number;
      };
    }
    
    class TokenSupplyPlanner {
      private readonly maxConcentration = 0.5; // Maximum allowed concentration
      private readonly minHolders = 100; // Minimum number of holders
      
      async planTokenSupply(
        mint: PublicKey,
        config: { limit?: number; commitment?: string }
      ): Promise<TokenSupplyPlan> {
        const accounts = await connection.getTokenLargestAccounts(mint, config);
        const amounts = accounts.value.map(acc => BigInt(acc.amount));
        const totalSupply = amounts.reduce((sum, x) => sum + x, BigInt(0));
        
        const recommendations: Array<{
          type: 'diversify' | 'consolidate' | 'rebalance';
          addresses: string[];
          reason: string;
        }> = [];
        
        // Check for concentration
        const top10Supply = amounts.slice(0, 10).reduce((sum, x) => sum + x, BigInt(0));
        const concentration = Number((top10Supply * BigInt(100)) / totalSupply) / 100;
        
        if (concentration > this.maxConcentration) {
          recommendations.push({
            type: 'diversify',
            addresses: accounts.value.slice(0, 10).map(acc => acc.address),
            reason: `Top 10 holders control ${(concentration * 100).toFixed(2)}% of supply`
          });
        }
        
        // Check for minimum holders
        if (accounts.value.length < this.minHolders) {
          recommendations.push({
            type: 'diversify',
            addresses: accounts.value.map(acc => acc.address),
            reason: `Only ${accounts.value.length} holders, below minimum of ${this.minHolders}`
          });
        }
        
        return {
          mint: mint.toBase58(),
          currentHolders: accounts.value.map((account, index) => ({
            address: account.address,
            amount: account.amount,
            rank: index + 1
          })),
          recommendations,
          metadata: {
            timestamp: Date.now()
          }
        };
      }
    }