En Solana, los sysvars son cuentas del sistema de solo lectura que dan a los programas de Solana acceso al estado de la blockchain, así como a la información de la red. Son similares a las variables globales de Ethereum, que también permiten a los contratos inteligentes acceder a la información del estado de la red o de la blockchain, pero tienen direcciones públicas únicas como las precompilaciones de Ethereum.
En los programas de Anchor, puedes acceder a los sysvars de dos maneras: ya sea usando el wrapper del método get de Anchor, o tratándolo como una cuenta en tu #[Derive(Accounts)], usando su dirección pública.
No todos los sysvars soportan el método get, y algunos están obsoletos (la información sobre su obsolescencia se especificará en esta guía). Para aquellos sysvars que no tienen un método get, accederemos a ellos usando su dirección pública.
- Clock: Usado para operaciones relacionadas con el tiempo, como obtener la hora actual o el número de slot.
- EpochSchedule: Contiene información sobre la programación de la epoch, incluyendo la epoch para un slot particular.
- Rent: Contiene la tasa de alquiler e información como los requisitos de saldo mínimo para mantener una cuenta exenta de alquiler.
- Fees: Contiene la calculadora de tarifas para el slot actual. La calculadora de tarifas proporciona información sobre cuántos lamports se pagan por firma en una transacción de Solana.
- EpochRewards: El sysvar EpochRewards mantiene un registro de la distribución de recompensas de la epoch en Solana, incluyendo las recompensas por bloque y las recompensas por staking.
- RecentBlockhashes: Contiene los hashes de bloques recientes activos.
- SlotHashes: Contiene el historial de los hashes de slots recientes.
- SlotHistory: Contiene un arreglo de slots disponibles durante la epoch más reciente en Solana, y se actualiza cada vez que se procesa un nuevo slot.
- StakeHistory: Mantiene un registro de las activaciones y desactivaciones de stake para toda la red por cada epoch, el cual se actualiza al comienzo de cada epoch.
- Instructions: Para obtener acceso a las instrucciones serializadas que forman parte de la transacción actual.
- LastRestartSlot: Contiene el número de slot del último reinicio (la última vez que Solana se reinició) o cero si nunca ha ocurrido ninguno. Si la blockchain de Solana se cayera y se reiniciara, una aplicación puede usar esta información para determinar si debe esperar hasta que las cosas se estabilicen.
Diferenciando entre slots y bloques de Solana.
Un slot es una ventana de tiempo (aproximadamente 400 ms) donde un líder designado puede producir un bloque. Un slot contiene un bloque (el mismo tipo de bloque que en Ethereum, es decir, una lista de transacciones). Sin embargo, un slot podría no contener un bloque si el líder del bloque falló en producir uno durante ese slot. Su relación se ilustra a continuación:

Aunque cada bloque se asigna exactamente a un slot, el hash del bloque no es el mismo que el hash del slot. Esta distinción es evidente al hacer clic en un número de slot en un explorador; se abren los detalles de un bloque con un hash diferente.
Tomemos un ejemplo de la imagen a continuación del explorador de bloques de Solana:

El número verde resaltado en la imagen es el número de slot 237240962, y el texto amarillo resaltado es el hash del slot DYFtWxEdLbos9E6SjZQCMq8z242Yv2bVoj6dzwskd5vZ. El hash del bloque resaltado en rojo a continuación es FzHwFHDAXJBc55rpjShznGCBnC7DsTCjxf3KKAk6hk9T.
(Otros detalles del bloque han sido recortados):

Podemos distinguir entre un bloque y un slot por sus hashes únicos, a pesar de que tengan los mismos números.
Como prueba, haz clic en cualquier número de slot en el explorador aquí y notarás que se abrirá la página de un bloque. Este bloque tendrá un hash diferente al hash del slot.
Accediendo a los Sysvars de Solana en Anchor, usando el método get
Como se mencionó anteriormente, no todos los sysvars pueden ser accedidos usando el método get de Anchor. Sysvars como Clock, EpochSchedule y Rent pueden ser accedidos usando este método.
Aunque la documentación de Solana incluye Fees y EpochRewards como sysvars a los que se puede acceder con el método get, estos están obsoletos en la última versión de Anchor. Por lo tanto, no pueden ser llamados usando el método get en Anchor.
Accederemos y registraremos los contenidos de todos los sysvars soportados actualmente utilizando el método get. Para empezar, creamos un nuevo proyecto en Anchor:
anchor init sysvars
cd sysvars
anchor build
Sysvar Clock
Para utilizar el sysvar Clock, podemos invocar el método Clock::get() (hicimos algo similar en un tutorial anterior) como se demuestra a continuación.
Agrega el siguiente código en la función initialize de nuestro proyecto:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// Get the Clock sysvar
let clock = Clock::get()?;
msg!(
"clock: {:?}",
// Retrieve all the details of the Clock sysvar
clock
);
Ok(())
}
Ahora, ejecuta la prueba en un nodo local de Solana y revisa el registro (log):

Sysvar EpochSchedule
Una epoch en Solana es un período de tiempo de aproximadamente dos días de duración. Solo se puede hacer o deshacer stake de SOL al comienzo de una epoch. Si haces stake (o unstake) de SOL antes del final de una epoch, el SOL se marca como “activating” o “deactivating” mientras espera que termine la epoch.
Solana describe esto con más detalle en su descripción sobre la delegación de SOL.
Podemos acceder al sysvar EpochSchedule usando el método get, de forma similar al sysvar Clock.
Actualiza la función initialize con el siguiente código:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// Get the EpochSchedule sysvar
let epoch_schedule = EpochSchedule::get()?;
msg!(
"epoch schedule: {:?}",
// Retrieve all the details of the EpochSchedule sysvar
epoch_schedule
);
Ok(())
}
Después de ejecutar la prueba nuevamente, se generará el siguiente registro (log):

A partir del registro, podemos observar que el sysvar EpochSchedule contiene los siguientes campos:
- slots_per_epoch resaltado en amarillo contiene el número de slots en cada epoch, que aquí es de 432,000 slots.
- leader_schedule_slot_offset resaltado en rojo determina el tiempo para el calendario del líder de la próxima epoch (habíamos hablado previamente sobre esto en el día 11). También está establecido en 432,000.
- warmup resaltado en púrpura es un booleano que indica si Solana está en la fase de calentamiento (warm-up). Durante esta fase, las epochs comienzan siendo más cortas y aumentan de tamaño gradualmente. Esto ayuda a que la red arranque sin problemas después de un reinicio o durante sus primeros días.
- first_normal_epoch resaltado en naranja identifica la primera epoch que puede tener su conteo completo de slots, y
first_normal_slotresaltado en azul es el slot que inicia esta epoch. En este caso ambos son 0 (cero).
La razón por la que vemos que first_normal_epoch y first_normal_slot son 0 es porque el validador de prueba no ha estado en ejecución durante dos días. Si ejecutáramos este comando en la mainnet (al momento de escribir este artículo), esperaríamos ver que first_normal_epoch fuera 576 y first_normal_slot fuera 248,832,000.

Sysvar Rent
Una vez más, usamos el método get para acceder al sysvar Rent.
Actualizamos la función initialize con el siguiente código:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// Previous code...
// Get the Rent sysvar
let rent_var = Rent::get()?;
msg!(
"Rent {:?}",
// Retrieve all the details of the Rent sysvar
rent_var
);
Ok(())
}
Ejecutamos la prueba y obtenemos este registro:

El sysvar Rent en Solana tiene tres campos clave:
- lamports_per_byte_year
- exemption_threshold
- burn_percent
El campo lamports_per_byte_year resaltado en amarillo indica el número de lamports requeridos por byte al año para la exención de alquiler.
El campo exemption_threshold resaltado en rojo es un multiplicador utilizado para calcular el saldo mínimo necesario para la exención de alquiler. En este ejemplo, vemos que necesitamos pagar lamports por byte para crear una nueva cuenta.
El 50% de eso se quema (burn_percent resaltado en púrpura) para gestionar la inflación de Solana.
El concepto de “rent” (alquiler) se explicará completamente en un tutorial posterior.
Accediendo a los Sysvars en Anchor Usando la Dirección Pública del Sysvar
Para los sysvars que no soportan el método get, podemos acceder a ellos usando sus direcciones públicas. Cualquier excepción a esto será especificada.
Sysvar StakeHistory
Recuerda que mencionamos anteriormente que este sysvar mantiene un registro de activaciones y desactivaciones de stake para toda la red por cada epoch. Sin embargo, dado que estamos ejecutando un nodo validador local, este sysvar devolverá datos vacíos.
Accederemos a este sysvar usando su dirección pública
SysvarStakeHistory1111111111111111111111111.
Primero, modificamos el struct de la cuenta Initialize en nuestro proyecto de la siguiente manera:
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK:
pub stake_history: AccountInfo<'info>, // We create an account for the StakeHistory sysvar
}
Le pedimos al lector que trate la nueva sintaxis como código base (boilerplate) por ahora. El /// CHECK: y AccountInfo se explicarán en un tutorial posterior. Para los curiosos, el token <'info> es un Rust lifetime.
A continuación, agregamos el siguiente código a la función initialize.
(La referencia a la cuenta del sysvar se pasará como parte de la transacción en nuestra prueba. Los ejemplos anteriores las tenían integradas en el framework de Anchor).
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(())
}
No estamos importando el sysvar StakeHistory porque podemos acceder a él mediante el uso del import super::*;. Si este no fuera el caso, importaríamos el sysvar específico.
Y actualizamos la prueba:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Sysvars } from "../target/types/sysvars";
describe("sysvars", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Sysvars as Program<Sysvars>;
// Create a StakeHistory PublicKey object
const StakeHistory_PublicKey = new anchor.web3.PublicKey(
"SysvarStakeHistory1111111111111111111111111"
);
it("Is initialized!", async () => {
// Add your test here.
const tx = await program.methods
.initialize()
.accounts({
stakeHistory: StakeHistory_PublicKey,
})
.rpc();
console.log("Your transaction signature", tx);
});
});
Ahora, volvemos a ejecutar nuestra prueba:

Tal como se mencionó anteriormente, devuelve datos vacíos para nuestro validador local.
También podemos obtener la clave pública del sysvar StakeHistory desde el cliente de Typescript de Anchor reemplazando nuestra variable StakeHistory_PublicKey con anchor.web3.SYSVAR_STAKE_HISTORY_PUBKEY.
Sysvar RecentBlockhashes
Cómo acceder a este sysvar se discutió en nuestro tutorial anterior. Como recordatorio, está obsoleto y se dejará de dar soporte.
Sysvar Fees
El sysvar Fees también está obsoleto.
Sysvar Instruction
Este sysvar se puede utilizar para acceder a las instrucciones serializadas de la transacción actual, junto con algunos metadatos que forman parte de esa transacción. Lo demostraremos a continuación.
Primero, actualizamos nuestras importaciones:
#[program]
pub mod sysvars {
use super::*;
use anchor_lang::solana_program::sysvar::{instructions, fees::Fees, recent_blockhashes::RecentBlockhashes};
// rest of the code
}
A continuación, agregamos la cuenta del sysvar Instruction al struct de la cuenta Initialize:
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK:
pub stake_history: AccountInfo<'info>, // We create an account for the StakeHistory sysvar
/// CHECK:
pub recent_blockhashes: AccountInfo<'info>,
/// CHECK:
pub instruction_sysvar: AccountInfo<'info>,
}
Ahora, modifica la función initialize para aceptar un parámetro number: u32 y agrega el siguiente código a la función initialize.
pub fn initialize(ctx: Context<Initialize>, number: u32) -> Result<()> {
// Previous code...
// Get Instruction sysvar
let arr = [ctx.accounts.instruction_sysvar.clone()];
let account_info_iter = &mut arr.iter();
let instructions_sysvar_account = next_account_info(account_info_iter)?;
// Load the instruction details from the instruction sysvar account
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(())
}
En contraste con el sysvar anterior, donde usamos <sysvar_name>::from_account_info() para recuperar el sysvar, en este caso, utilizamos el método load_instruction_at_checked() del sysvar Instruction. Este método requiere el índice de datos de la instrucción (0 en este caso) y la cuenta del sysvar Instruction como parámetros.
Actualiza la prueba:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Sysvars } from "../target/types/sysvars";
describe("sysvars", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Sysvars as Program<Sysvars>;
// Create a StakeHistory PublicKey object
const StakeHistory_PublicKey = new anchor.web3.PublicKey(
"SysvarStakeHistory1111111111111111111111111"
);
it("Is initialized!", async () => {
// Add your test here.
const tx = await program.methods
.initialize(3) // Call the initialze function with the number `3`
.accounts({
stakeHistory: StakeHistory_PublicKey, // pass the public key of StakeHistory sysvar to the list of accounts needed for the instruction
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY, // pass the public key of RecentBlockhashes sysvar to the list of accounts needed for the instruction
instructionSysvar: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY, // Pass the public key of the Instruction sysvar to the list of accounts needed for the instruction
})
.rpc();
console.log("Your transaction signature", tx);
});
});
Y ejecuta la prueba:

Si examinamos detenidamente el registro, podemos ver el Program ID, la clave pública de la instrucción del sysvar, los datos serializados y otros metadatos.
También podemos ver el número 3 resaltado con la flecha amarilla tanto en los datos de la instrucción serializada como en el registro de nuestro propio programa. Los datos serializados resaltados en rojo son un discriminador inyectado por Anchor (podemos ignorarlo).
Ejercicio: Accede al sysvar LastRestartSlot
SysvarLastRestartS1ot1111111111111111111111 usando el método que se utilizó anteriormente. Ten en cuenta que Anchor no tiene la dirección para este sysvar, por lo que necesitarás crear un objeto PublicKey.
Sysvars de Solana a los que no se puede acceder en la versión actual de Anchor.
En la versión actual de Anchor, no es factible acceder a ciertos sysvars. Estos sysvars incluyen EpochRewards, SlotHistory y SlotHashes. Al intentar acceder a estos sysvars, se produce un error.
Aprende más con RareSkills
Consulta nuestro curso de Solana para ver más tutoriales sobre Solana; este tutorial es parte de ese curso.
Publicado originalmente el 19 de febrero de 2024