This repository contains a custom syscall implementation for the Solana blockchain runtime (Agave validator) that enables bulk fetching of multiple sysvars in a single call, reducing overhead and improving on-chain efficiency for programs needing multiple sysvar data.
Solana programs commonly access sysvars like Clock, Rent for now but can add other also like EpochSchedule etc. Each access is a separate syscall that incurs overhead. This custom syscall:
-
Accepts a list of requested sysvar IDs.
-
Returns all requested sysvars concatenated and serialized in one response.
-
Simplifies sysvar data retrieval and reduces cross-program invocation complexity.
-
Serves as a learning exercise demonstrating how to write syscalls inside the Solana runtime (Agave).
This syscall is designed for development, testing, and for study only and is not recommended for mainnet usage
-
Structured bulk retrieval of multiple sysvars.
-
Uses Rust and the Solana validator internal libraries.
-
Demonstrates syscall registration, argument parsing, and serialization inside the Agave repo.
Custom Syscall Code Snippet (Agave Runtime)Source Link
declare_builtin_function!(
SyscallBulkSysvarFetch,
fn rust(
invoke_context: &mut InvokeContext,
arg1: u64,
arg2: u64,
arg3: u64,
arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
// Pointer and length of sysvar ID list in guest memory
let sysvar_list_ptr = arg1 as *const u8;
let sysvar_list_len = arg2 as usize;
// Translate guest memory to host slice
let sysvar_list_data = invoke_context.get_memory_slice(sysvar_list_ptr, sysvar_list_len, memory_mapping)
.ok_or_else(|| Error::AccountDataTooSmall)?;
// Cast to byte slice of sysvar IDs
let sysvar_ids: &[u8] = sysvar_list_data;
// Output buffer pointer and length
let output_ptr = arg3 as *mut u8;
let output_len = arg4 as usize;
let output_buffer = invoke_context.get_memory_slice_mut(output_ptr, output_len, memory_mapping)
.ok_or_else(|| Error::AccountDataTooSmall)?;
let mut cursor = std::io::Cursor::new(output_buffer);
for &sysvar_id in sysvar_ids {
match sysvar_id {
0 => {
// Clock sysvar
let clock = invoke_context.clock_sysvar()?;
let clock_bytes = bincode::serialize(&clock).map_err(|_| Error::AccountDataTooSmall)?;
cursor.write_all(&clock_bytes).map_err(|_| Error::AccountDataTooSmall)?;
}
1 => {
// Rent sysvar
let rent = invoke_context.rent_sysvar()?;
let rent_bytes = bincode::serialize(&rent).map_err(|_| Error::AccountDataTooSmall)?;
cursor.write_all(&rent_bytes).map_err(|_| Error::AccountDataTooSmall)?;
}
// Add more sysvars as needed...
_ => return Err(Error::InvalidArgument),
}
}
// Return total bytes written to the output buffer
Ok(cursor.position() as u64)
}
)
- Set up the Agave validator with the custom syscall:
cd path/to/agave
cargo build --release
cargo run -p agave-validator --bin solana-test-validator
- Build your custom on-chain program that calls this syscall:
cd path/to/your/program
cargo build-sbf
- Deploy your program locally:
solana config set --url localhost
cargo run -p solana-cli -- program deploy /path/to/your_program.so --program-id /path/to/your_program-keypair.json
- Run tests or interact with the program calling bulk_sysvar_fetch.