在 Anchor Rust 中读取账户余额
要在 Solana 程序中读取某个地址的 Solana 余额,请使用以下代码:
use anchor_lang::prelude::*;
declare_id!("Gnf6u7S7fGJbqEGH9PuDE5Prq6f6ZrDxHY3jNJ4SYySQ");
#[program]
pub mod balance {
use super::*;
pub fn read_balance(ctx: Context<ReadBalance>) -> Result<()> {
let balance = ctx.accounts.acct.to_account_info().lamports();
msg!("balance in Lamports is {}", balance);
Ok(())
}
}
#[derive(Accounts)]
pub struct ReadBalance<'info> {
/// CHECK: although we read this account's balance, we don't do anything with the information
pub acct: UncheckedAccount<'info>,
}
以下是用于触发它的 web3 js 代码:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Balance } from "../target/types/balance";
describe("balance", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Balance as Program<Balance>;
// the following is the Solana wallet we are using
let pubkey = new anchor.web3.PublicKey("5jmigjgt77kAfKsHri3MHpMMFPo6UuiAMF19VdDfrrTj");
it("Tests the balance", async () => {
const tx = await program.methods.readBalance().accounts({ acct: pubkey }).rpc();
});
});
此示例中的一些内容与之前的教程有所不同,尤其是 UncheckedAccount 的使用。
Solana Anchor 中的 UncheckedAccount 是什么?
UncheckedAccount 类型告诉 Anchor 不要检查正在被读取的账户是否归该程序所有。
请注意,我们通过 Context 结构体传递的账户并不是由该程序初始化的账户,因此该程序并不拥有它。
当 Anchor 在 #[derive(Accounts)] 中读取 Account 类型的账户时,它会(在幕后)检查该账户是否归该程序所有。如果不是,执行将中止。
这是一个重要的安全检查。
如果恶意用户伪造了一个非该程序创建的账户,然后将其传递给 Solana 程序,而 Solana 程序盲目信任该账户中的数据,则可能会发生严重错误。
例如,如果该程序是一个银行,且账户中存储着用户的余额,那么黑客就可以提供一个不同的账户,该账户内的余额被人为篡改得比他们实际拥有的更高。
然而,要实施这种攻击,用户必须在一个单独的交易中创建虚假账户,然后将其传递给 Solana 程序。但是,Anchor 框架会在幕后检查该账户是否不归程序所有,并拒绝读取该账户。
UncheckedAccount 绕过了这项安全检查。
重要提示:AccountInfo 和 UncheckedAccount 互为别名,并且 AccountInfo 具有相同的安全注意事项。
在我们的例子中,我们传入的账户显然不归程序所有——我们想要检查任意账户的余额。因此,我们必须确定在移除此安全检查后,不会有任何关键逻辑被篡改。
在我们的例子中,我们只是将余额记录到控制台,但在大多数现实世界的用例中,会有更复杂的逻辑。
/// CHECK: 是什么?
由于使用 UncheckedAccount 存在危险,Anchor 强制要求你包含此注释,以促使你不要忽视安全注意事项。
练习:移除 /// CHECK: 注释并运行 anchor build,你应该会看到构建中止,并要求你将注释加回来,同时解释为什么这个 Unchecked Account 是安全的。也就是说,读取一个不受信任的账户可能是危险的,Anchor 希望确保你没有使用该账户中的数据执行任何关键操作。
为什么程序中没有 #[account] 结构体?
#[account] 结构体告诉 Anchor 如何反序列化保存数据的账户。例如,像下面这样的账户结构体会通知 Anchor,它应该将存储在账户中的数据反序列化为单个 u64:
#[account]
pub struct Counter {
counter: u64
}
然而,在我们的例子中,我们并没有读取账户中的数据——我们只是在读取余额。这与我们可以读取 Ethereum 地址的余额但不读取其任何代码的方式类似。由于我们不想反序列化数据,因此我们不提供 #[account] 结构体。
并非账户中的所有 SOL 都是可花费的
回想一下我们在讨论 Solana 账户租金 时提到的内容,账户必须保持一定余额的 SOL 才能获得“租金豁免”(rent exempt),否则运行时将会删除该账户。仅仅因为账户中有“1 SOL”,并不一定意味着该账户可以花掉全部的 1 SOL。
例如,如果你正在构建一个质押或银行应用程序,用户的已存 SOL 保存在独立的账户中,那么简单地测量这些账户的 SOL 余额是不准确的,因为租金将包含在余额之中。
通过 RareSkills 了解更多
请参阅我们的 Solana 开发者课程 以获取更多 Solana 学习资料。
最初发布于 2024 年 2 月 29 日