You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Web3.js在Solana链上获取代币元数据(已获取代币账户信息)

获取Solana代币元数据的方法

好问题!既然你已经通过getParsedTokenAccountsByOwner拿到了非零余额的代币账户,接下来要获取对应代币的元数据,核心是利用每个代币账户里的mint地址——Solana上的代币元数据(比如名称、符号、图标链接等)基本都和代币的mint账户绑定,通常存储在Metaplex的元数据程序中。

下面给你两种常用的实现方式,你可以根据项目需求选择:

方法一:使用Metaplex JS SDK(推荐,简化开发)

Metaplex提供了现成的工具处理元数据,不用自己手动解析复杂的账户结构。

首先确保安装依赖:

npm install @metaplex-foundation/js @solana/web3.js

然后你可以修改现有代码,在拿到nonZeroAccounts后遍历获取元数据:

import { Connection, PublicKey, TOKEN_PROGRAM_ID } from '@solana/web3.js';
import { Metaplex } from '@metaplex-foundation/js';

// 假设你已经初始化好connection和用户publicKey
const connection = new Connection('https://api.mainnet-beta.solana.com');
const metaplex = Metaplex.make(connection);

connection.getParsedTokenAccountsByOwner(
  new PublicKey(publicKey.toBase58()),
  { programId: TOKEN_PROGRAM_ID }
)
.then(async (b) => {
  const nonZeroAccounts = b?.value?.filter(
    (obj) => obj.account.data.parsed.info.tokenAmount.uiAmount > 0
  );

  // 遍历每个代币账户,获取对应的元数据
  const tokensWithMetadata = await Promise.all(
    nonZeroAccounts.map(async (tokenAccount) => {
      const mintAddress = new PublicKey(tokenAccount.account.data.parsed.info.mint);
      // 通过mint地址获取元数据
      const metadata = await metaplex.nfts().findByMint({ mintAddress });
      
      return {
        tokenAccountInfo: tokenAccount,
        tokenMetadata: {
          name: metadata.name,
          symbol: metadata.symbol,
          iconUrl: metadata.json?.image,
          description: metadata.json?.description,
          uri: metadata.uri
        }
      };
    })
  );

  setTokens(JSON.stringify(tokensWithMetadata, null, 2));
})
.catch(err => console.error('获取元数据出错:', err));

这个方法的优势是SDK帮你处理了元数据账户地址推导和数据解析,直接就能拿到结构化的代币信息,省了很多手动编码的麻烦。

方法二:手动获取元数据(不依赖SDK,更底层)

如果你不想引入额外依赖,可以手动计算元数据账户地址,再调用getAccountInfo解析数据。

元数据账户地址是通过Metaplex的PDA规则生成的:用元数据程序ID(metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s)和mint地址作为种子推导。

代码示例如下:

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

const METADATA_PROGRAM_ID = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');

connection.getParsedTokenAccountsByOwner(
  new PublicKey(publicKey.toBase58()),
  { programId: TOKEN_PROGRAM_ID }
)
.then(async (b) => {
  const nonZeroAccounts = b?.value?.filter(
    (obj) => obj.account.data.parsed.info.tokenAmount.uiAmount > 0
  );

  const tokensWithMetadata = await Promise.all(
    nonZeroAccounts.map(async (tokenAccount) => {
      const mintAddress = new PublicKey(tokenAccount.account.data.parsed.info.mint);
      
      // 推导元数据账户的PDA地址
      const [metadataAddress] = await PublicKey.findProgramAddress(
        [Buffer.from('metadata'), METADATA_PROGRAM_ID.toBuffer(), mintAddress.toBuffer()],
        METADATA_PROGRAM_ID
      );

      // 获取元数据账户信息
      const metadataAccount = await connection.getAccountInfo(metadataAddress);
      if (!metadataAccount) {
        return { tokenAccountInfo: tokenAccount, tokenMetadata: null };
      }

      // 手动解析元数据核心字段(完整结构可参考Metaplex文档)
      const data = metadataAccount.data;
      const nameLength = data.readUInt32LE(64);
      const name = data.slice(68, 68 + nameLength).toString('utf8');
      const symbolLength = data.readUInt32LE(68 + nameLength);
      const symbol = data.slice(72 + nameLength, 72 + nameLength + symbolLength).toString('utf8');
      const uriLength = data.readUInt32LE(72 + nameLength + symbolLength);
      const uri = data.slice(76 + nameLength + symbolLength, 76 + nameLength + symbolLength + uriLength).toString('utf8');

      // 如果需要获取uri里的详细内容(比如图片、描述),需要发起HTTP请求
      let detailedMetadata = null;
      if (uri) {
        try {
          // 处理IPFS链接,添加网关前缀
          const accessibleUri = uri.startsWith('ipfs://') ? uri.replace('ipfs://', 'https://ipfs.io/ipfs/') : uri;
          detailedMetadata = await fetch(accessibleUri).then(res => res.json());
        } catch (err) {
          console.error(`解析uri内容失败: ${err}`);
        }
      }

      return {
        tokenAccountInfo: tokenAccount,
        tokenMetadata: {
          name,
          symbol,
          uri,
          ...detailedMetadata
        }
      };
    })
  );

  setTokens(JSON.stringify(tokensWithMetadata, null, 2));
})
.catch(err => console.error('获取元数据出错:', err));

这种方法更灵活,但需要自己处理数据解析,适合不需要完整SDK功能的轻量场景。

注意事项

  • 少数代币可能不遵循Metaplex元数据标准,这种情况下会获取不到信息,但绝大多数主流代币都适配这个标准。
  • 如果元数据的uri是IPFS链接,需要替换成可访问的网关地址才能获取内容。

内容的提问来源于stack exchange,提问作者matt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:57:50