跳转到主要内容

参数

delegate
string
必填
代理人的公钥(base-58 编码)
config
object
必填
包含以下内容的配置对象:
programId
string
必填
代币程序的公钥(base-58 编码)
commitment
string
Commitment 级别(processed、confirmed、finalized)
encoding
string
账户数据的编码格式(base58、base64、jsonParsed)
dataSlice
object
offset
number
账户数据的偏移量
length
number
要返回的数据长度

响应

result
object
包含以下内容的对象:
context
object
slot
number
处理请求时的 slot
value
array
代币账户对象数组,包含:
pubkey
string
代币账户的公钥(base-58 编码)
account
object
lamports
number
账户中的 lamports 数量
owner
string
账户所有者的公钥(base-58 编码)
executable
boolean
账户是否可执行
rentEpoch
number
账户下次需要支付租金的 epoch
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. 根据需求使用适当的 commitment 级别
  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;
        }> = [];
        
        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;
      }
    }
    
  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]) => {
          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()
          }
        };
      }
    }