在 Solana 中,系统变量(sysvars)是只读的系统账户,为程序提供区块链状态和网络信息的访问权限。它们类似于以太坊的全局变量,但每个系统变量拥有唯一的公钥地址,类似以太坊的预编译合约。
在 Solana 中,系统变量(sysvars)是只读的系统账户,为程序提供区块链状态和网络信息的访问权限。它们类似于以太坊的全局变量,但每个系统变量拥有唯一的公钥地址,类似以太坊的预编译合约。
在 Anchor 中,访问系统变量有两种方式:
#[derive(Accounts)]
中通过公钥地址将其作为账户引用。并非所有系统变量都支持 get 方法,且部分已被弃用(下文将注明)。对于无 get 方法的系统变量,需通过公钥访问。
以下是常见的 Solana 系统变量:
槽(slot)是约 400 毫秒的时间窗口,领导者在此期间可生成区块。一个槽通常对应一个区块(包含交易列表),但若领导者未生成区块,槽可能为空。通过 Solana 浏览器 查看,槽与区块的哈希始终不同。
支持 get 方法的系统变量包括 Clock、EpochSchedule 和 Rent。Fees 和 EpochRewards 虽在 Solana 文档中列为可访问,但在最新 Anchor 版本中已弃用。
创建新 Anchor 项目:

anchor init sysvars
cd sysvars
anchor build
更新 lib.rs:

use anchor_lang::prelude::*;
declare_id!("DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL");
#[program]
pub mod sysvars {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let clock = Clock::get()?;
msg!("clock: {:?}",clock);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
运行 anchor test,日志显示 Clock 的字段(如 unix_timestamp、slot 等)。

Transaction executed in slot 6:
Signature: 2VNMBwFkpvbiL1cdn2K1Xb2gUX4Zt9EnwxFTSabAS3WYxPUFhhuZqygu3bH4NTNfkhm7y6Cgw4dLU5sWaFbjHph1
Status: Ok
Log Messages:
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL invoke [1]
Program log: Instruction: Initialize
Program log: clock: Clock { slot: 6, epoch_start_timestamp: 1741833575, epoch: 0, leader_schedule_epoch: 1, unix_timestamp: 1741833577 }
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL consumed 3853 of 200000 compute units
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL success
纪元(epoch)约为两天,质押或赎回 SOL 需在纪元边界生效(详见 Solana 质押)。
更新 initialize:

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let epoch_schedule = EpochSchedule::get()?;
msg!("epoch schedule: {:?}",epoch_schedule);
Ok(())
}
日志示例:

Transaction executed in slot 3:
Signature: 5K8Nd27QHGABWSHSHqHLQri4mQYviHcb5PtkBXYzERLXLfcMbrZD7fJZLvJHVVXmVEuB4Ly8htUptMsM2UFwnq4w
Status: Ok
Log Messages:
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL invoke [1]
Program log: Instruction: Initialize
Program log: epoch schedule: EpochSchedule { slots_per_epoch: 432000, leader_schedule_slot_offset: 432000, warmup: false, first_normal_epoch: 0, first_normal_slot: 0 }
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL consumed 3447 of 200000 compute units
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL success
更新 initialize:

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let rent_var = Rent::get()?;
msg!("Rent {:?}",rent_var);
Ok(())
}
日志示例:

Transaction executed in slot 3:
Signature: 5Fy1zqmj1UaU4ggaFipm4c6CNrGWUADBuebMHzmc9WfwAhoBteMg1g4rP42pbAWYoydyJnMXVgR1ftijcVRXLU4u
Status: Ok
Log Messages:
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL invoke [1]
Program log: Instruction: Initialize
Program log: Rent Rent { lamports_per_byte_year: 3480, exemption_threshold: 2.0, burn_percent: 50 }
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL consumed 3497 of 200000 compute units
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL success
租金详情将在后续文章中展开。
对于无 get 方法的系统变量,需通过公钥在 #[derive(Accounts)] 中引用。
记录网络质押历史,由于我们使用的是本地验证器,所以将返回空数据。公钥为 SysvarStakeHistory1111111111111111111111111。
更新 lib.rs:

use anchor_lang::prelude::*;
declare_id!("DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL");
#[program]
pub mod sysvars {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// Previous code...
// Accessing the StakeHistory sysvar
// Create an array to store the StakeHistory account
let arr = [ctx.accounts.stake_history.clone()];
// Create an iterator for the array
let accounts_iter = &mut arr.iter();
// Get the next account info from the iterator (still StakeHistory)
let sh_sysvar_info = next_account_info(accounts_iter)?;
// Create a StakeHistory instance from the account info
let stake_history = StakeHistory::from_account_info(sh_sysvar_info)?;
msg!("stake_history: {:?}", stake_history);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK:
pub stake_history: AccountInfo<'info>,
}
更新测试 tests/sysvars.ts:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Sysvars } from "../target/types/sysvars";
describe("sysvars", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Sysvars as Program<Sysvars>;
const StakeHistory_PublicKey = new anchor.web3.PublicKey(
"SysvarStakeHistory1111111111111111111111111"
);
it("Is initialized!", async () => {
const tx = await program.methods
.initialize()
.accounts({
stakeHistory: StakeHistory_PublicKey,
})
.rpc();
console.log("Your transaction signature", tx);
});
});
日志显示空数据(本地验证器未记录质押历史)。

Transaction executed in slot 3:
Signature: 3viDgr1ycjNsQSxS2iC55RdQu19qpbuF7cAcVxfeLjeiHCEzS4mdMqiCiKPDpJqFqJ5j3RFaHysS7zBh9eUNeKhr
Status: Ok
Log Messages:
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL invoke [1]
Program log: Instruction: Initialize
Program log: stake_history: StakeHistory([])
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL consumed 1576 of 200000 compute units
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL success
提供当前交易的序列化指令及元数据。更新 lib.rs:

use anchor_lang::prelude::*;
use anchor_lang::solana_program::sysvar::{instructions, fees::Fees, recent_blockhashes::RecentBlockhashes};
declare_id!("DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL");
#[program]
pub mod sysvars {
use super::*;
pub fn initialize(ctx: Context<Initialize>, number: u32) -> Result<()> {
let arr = [ctx.accounts.instruction_sysvar.clone()];
let account_info_iter = &mut arr.iter();
let instructions_sysvar_account = next_account_info(account_info_iter)?;
let instruction_details =
instructions::load_instruction_at_checked(0, instructions_sysvar_account)?;
msg!(
"Instruction details of this transaction: {:?}",
instruction_details
);
msg!("Number is: {}", number);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK:
pub stake_history: AccountInfo<'info>,
/// CHECK:
pub recent_blockhashes: AccountInfo<'info>,
/// CHECK:
pub instruction_sysvar: AccountInfo<'info>,
}
更新测试:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Sysvars } from "../target/types/sysvars";
describe("sysvars", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Sysvars as Program<Sysvars>;
const StakeHistory_PublicKey = new anchor.web3.PublicKey(
"SysvarStakeHistory1111111111111111111111111"
);
it("Is initialized!", async () => {
const tx = await program.methods
.initialize(3)
.accounts({
stakeHistory: StakeHistory_PublicKey,
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
instructionSysvar: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
})
.rpc();
console.log("Your transaction signature", tx);
});
});
日志显示程序 ID、指令数据及输入参数(如 number: 3)。
仔细查看日志,可以观察到程序 ID、系统变量指令的公钥、序列化数据以及其他元数据。
此外,在序列化指令数据和我们自定义的程序日志中,能够看到数字 3。而前面的序列化数据是 Anchor 注入的鉴别器。

Transaction executed in slot 3:
Signature: 3F1jxLj3phq58F1Yi4y8mTWZLE156di1AH7asNzy5rdbNdNctrGczo1oc9QMWfKGPc6eZFpE3j2NtqN8nd7Qy4rM
Status: Ok
Log Messages:
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL invoke [1]
Program log: Instruction: Initialize
Program log: Instruction details of this transaction: Instruction { program_id: DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL, accounts: [AccountMeta { pubkey: SysvarStakeHistory1111111111111111111111111, is_signer: false, is_writable: false }, AccountMeta { pubkey: SysvarRecentB1ockHashes11111111111111111111, is_signer: false, is_writable: false }, AccountMeta { pubkey: Sysvar1nstructions1111111111111111111111111, is_signer: false, is_writable: false }], data: [175, 175, 109, 31, 13, 152, 155, 237, 3, 0, 0, 0] }
Program log: Number is: 3
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL consumed 55224 of 200000 compute units
Program DfnoLKzEct937xzk3a3gW2WSwD2tGidFNq9xxuN76QvL success
EpochRewards、SlotHistory 和 SlotHashes 在最新 Anchor 版本中无法访问,尝试调用会报错。
【笔记配套代码】
https://github.com/0xE1337/rareskills_evm_to_solana
【参考资料】
https://learnblockchain.cn/column/119
https://www.rareskills.io/solana-tutorial
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!