文章介绍了如何使用 getParsedProgramAccounts 方法查询 Solana 钱包中的所有代币账户及其余额,并提供了详细的代码示例和步骤。
你好,读者们!为了启动 Solana Summer 并响应当前的白名单热门话题,我们认为深入了解你和你的用户使用 getParsedProgramAccounts 方法的所有Token账户会很有帮助。这个工具对于查询 Solana 上的不同程序非常方便,包括 Solana SPL Token Account 库。
更喜欢视频演示?跟随 Noah 学习如何在 13 分钟内获取 Solana 钱包持有的所有Token。
本指南将指导你创建一个简单的脚本,查询 Solana 的主网并返回该钱包拥有的所有 Token Accounts 及其账户余额。
程序:Solana 上的智能合约。在这个例子中,我们将使用 SPL Token Program,它定义了可替代和不可替代代币在 Solana 上的典型用例。
程序过滤器:许多链上程序查询会提取大量数据集,因此缩小搜索范围至关重要。我们将使用 GetProgramAccountsFilter 类型来帮助我们缩小搜索范围。
在终端中创建一个新的项目目录和文件,index.ts,命令如下:
mkdir sol-get-accounts
cd sol-get-accounts
echo > index.ts
初始化你的项目:
yarn init --yes
或者
npm init --yes
安装 Solana Web3 依赖项:
yarn add @solana/web3.js@1 @solana/spl-token
或者
npm install @solana/web3.js@1 @solana/spl-token
在代码编辑器中打开 index.ts,并添加以下依赖项:
import { Connection, GetProgramAccountsFilter } from "@solana/web3.js";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
建立与 Solana 主网的连接
要在 Solana 上进行开发,你需要一个API端点以连接到网络。你可以使用公共节点或部署和管理你自己的基础设施,但是如果你希望获得 8 倍的响应时间,欢迎让我们来处理繁重的工作。
确保在 Solana 主网启动你的节点,并复制 HTTP 链接:
const rpcEndpoint = 'https://example.solana-mainnet.quiknode.pro/000000/';
const solanaConnection = new Connection(rpcEndpoint);
将要查询的钱包定义为字符串:
const walletToQuery = 'YOUR_PUBLIC_KEY'; //示例: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
很好!我们准备构建我们的Token查询。
创建一个新的 async 函数 getTokenAccounts,并需要两个参数 wallet 和 solanaConnection:
async function getTokenAccounts(wallet: string, solanaConnection: Connection) {
}
首先,让我们定义我们的过滤器。我们将使用的过滤器如下:
dataSize 是一个用于查找特定大小账户的过滤器。对于Token账户,这是一个已知数量,165。
memcmp 或 “内存比较” 过滤器,用于在账户中缩小搜索范围。具体来说,我们将使用 offset 来确定在我们账户的 165 字节中要搜索的位置(对于该程序,已知值:所有者的公共密钥从 32 开始)和 bytes 来设置我们要搜索的内容:在这种情况下,用户的钱包地址。
有关 SPL Token账户数据的源信息,请查看 这里。
在 getTokenAccounts 中定义一个名为 filters 的变量。
const filters:GetProgramAccountsFilter[] = [\
{\
dataSize: 165, //账户大小(字节)\
},\
{\
memcmp: {\
offset: 32, //我们在账户中查询的位置(字节)\
bytes: wallet, //我们的搜索条件,一个 base58 编码字符串\
}\
}\
];
这应该将我们的查询缩小为仅查找由我们的钱包拥有的Token账户。
现在调用 getParsedProgramAccounts 方法,并传入 SPL Token Program ID 和我们的 filters。
const accounts = await solanaConnection.getParsedProgramAccounts(
TOKEN_PROGRAM_ID, //SPL Token Program, new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
{filters: filters}
);
之前的调用返回一个所有匹配的Token账户数组,具有以下结构:
{
pubkey: PublicKey, //Token账户的公共密钥
account: AccountInfo //包含有关我们Token账户信息的对象
}[]
你可以记录 accounts.length 以查看用户拥有多少个Token账户:
console.log(`Found ${accounts.length} token account(s) for wallet ${wallet}.`);
构建一个简单的 forEach 循环来迭代我们的结果并记录我们的结果。在你的 getTokenAccounts 函数中添加:
accounts.forEach((account, i) => {
//解析账户数据
const parsedAccountInfo:any = account.account.data;
const mintAddress:string = parsedAccountInfo["parsed"]["info"]["mint"];
const tokenBalance: number = parsedAccountInfo["parsed"]["info"]["tokenAmount"]["uiAmount"];
//记录结果
console.log(`Token Account No. ${i + 1}: ${account.pubkey.toString()}`);
console.log(`--Token Mint: ${mintAddress}`);
console.log(`--Token Balance: ${tokenBalance}`);
});
最后,调用 getTokenAccounts(walletToQuery,solanaConnection)。你的最终脚本应该如下所示:
import { Connection, GetProgramAccountsFilter } from "@solana/web3.js";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
const rpcEndpoint = 'https://example.solana-mainnet.quiknode.pro/000000/';
const solanaConnection = new Connection(rpcEndpoint);
const walletToQuery = 'YOUR_PUBLIC_KEY'; //示例: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
async function getTokenAccounts(wallet: string, solanaConnection: Connection) {
const filters:GetProgramAccountsFilter[] = [\
{\
dataSize: 165, //账户大小(字节)\
},\
{\
memcmp: {\
offset: 32, //我们在账户中查询的位置(字节)\
bytes: wallet, //我们的搜索条件,一个 base58 编码字符串\
},\
}];
const accounts = await solanaConnection.getParsedProgramAccounts(
TOKEN_PROGRAM_ID, //new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
{filters: filters}
);
console.log(`Found ${accounts.length} token account(s) for wallet ${wallet}.`);
accounts.forEach((account, i) => {
//解析账户数据
const parsedAccountInfo:any = account.account.data;
const mintAddress:string = parsedAccountInfo["parsed"]["info"]["mint"];
const tokenBalance: number = parsedAccountInfo["parsed"]["info"]["tokenAmount"]["uiAmount"];
//记录结果
console.log(`Token Account No. ${i + 1}: ${account.pubkey.toString()}`);
console.log(`--Token Mint: ${mintAddress}`);
console.log(`--Token Balance: ${tokenBalance}`);
});
}
getTokenAccounts(walletToQuery,solanaConnection);
运行 ts-node index.ts,你应该在终端中看到如下日志:
玩得开心吗?想尝试更多过滤器吗?比如说,你想查看钱包中是否包含特定铸币的Token。你可以在上面的 forEach 循环中包括该过滤器,但你可能考虑将其添加到你原始查询中的 filters 变量,以降低搜索时间。下面是如何做到的。
在你应用程序顶部的定义中,添加要搜索的铸币地址,例如:
const MINT_TO_SEARCH = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'; //USDC 代币地址
现在回到我们的 getTokenAccounts 函数,向 filters 添加一个额外的过滤器,以在字节位置 0(该程序中铸币公共密钥的已知位置)查找 _MINT_TOSEARCH:
const filters:GetProgramAccountsFilter[] = [\
{\
dataSize: 165, //账户大小(字节)\
},\
{\
memcmp: {\
offset: 32, //我们在账户中查询的位置(字节)\
bytes: wallet, //我们的搜索条件,一个 base58 编码字符串\
},\
},\
//添加这个搜索参数\
{\
memcmp: {\
offset: 0, //字节数\
bytes: MINT_TO_SEARCH, //base58 编码字符串\
},\
}];
重新运行你的代码,你应该发现结果现在仅限于你搜索的铸币:
你完成了!现在你应该理解如何查询钱包的所有Token账户,并拥有一个坚实的基础,以便将来能够查询其他 Solana 程序。
觉得这个有用吗?在 这里 查看我们其他的 Solana 教程。订阅我们的 通讯 获取更多关于 Solana 的文章和指南。如果你有任何反馈,欢迎通过 Twitter 与我们联系。你也可以随时在我们的 Discord 社区服务器上与我们聊天,那里有一些你见过的最炫酷的开发者 :)
告诉我们 如果你有任何反馈或对新主题的请求。我们很想听到你的意见。
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!