~bpf.sol | 0x04: Sealevel Syscalls
This page lists the syscalls available in the Sealevel VM.
Syscalls allow programs running in the Solana virtual machine to interact with outside resources. They can be invoked using the call
instruction with the immediate value set to the symbol hash.
Syscall Table
Hash | Symbol |
---|---|
b6fc1a11 | abort |
686093bb | sol_panic_ |
207559bd | sol_log_ |
5c2a3178 | sol_log_64_ |
52ba5096 | sol_log_compute_units_ |
7ef088ca | sol_log_pubkey |
9377323c | sol_create_program_address |
48504a38 | sol_try_find_program_address |
11f49d86 | sol_sha256 |
d7793abb | sol_keccak256 |
17e40350 | sol_secp256k1_recover |
174c5122 | sol_blake3 |
aa2607ca | sol_curve_validate_point |
dd1c41a6 | sol_curve_group_op |
d56b5fe9 | sol_get_clock_sysvar |
23a29a61 | sol_get_epoch_schedule_sysvar |
3b97b73c | sol_get_fees_sysvar |
bf7188f6 | sol_get_rent_sysvar |
717cc4a3 | sol_memcpy_ |
434371f8 | sol_memmove_ |
5fdcde31 | sol_memcmp_ |
3770fb22 | sol_memset_ |
a22b9c85 | sol_invoke_signed_c |
d7449092 | sol_invoke_signed_rust |
83f00e8f | sol_alloc_free_ |
a226d3eb | sol_set_return_data |
5d2245e4 | sol_get_return_data |
7317b434 | sol_log_data |
adb8efc8 | sol_get_processed_sibling_instruction |
85532d94 | sol_get_stack_height |
Calling Convention
The convention for invoking syscalls follows the SBFv2 ABI for function calls.
Input params are provided in registers r1
, r2
, r3
, r4
, r5
.
Register r0
is always set, either to the return value, or zero for void syscalls.
Other registers are preserved. Syscalls do not allocate a call frame.
If the caller provides invalid inputs that cause a runtime error (e.g. calling sol_memcpy_
on null pointer), the VM raises the error at the call site.
Syscall Reference
abort
See abort(3)
. Aborts the VM with SyscallError::Abort
and never returns.
#include <stdlib.h>
noreturn void abort();
sol_panic_
Aborts the VM with SyscallError::Panic
and never returns.
#include <sol/assert.h>
noreturn void sol_panic_(/* r1 */ const char *file_str,
/* r2 */ uint64_t file_str_len,
/* r3 */ uint64_t line,
/* r4 */ uint64_t column);
sol_log_
Writes a log entry to the Sealevel logging facility.
#include <sol/log.h>
void sol_log_(/* r1 */ const char *message,
/* r2 */ uint64_t len);
sol_log_64_
Writes a log entry containing five 64-bit integers to the Sealevel logging facility. The log message uses format Program log: %x %x %x %x %x
.
#include <sol/log.h>
void sol_log_64_(uint64_t r1,
uint64_t r2,
uint64_t r3,
uint64_t r4,
uint64_t r5);
sol_log_compute_units_
Writes the current compute unit consumption to the Sealevel logging facility.
The log message uses format Program consumption: %d units remaining
#include <sol/log.h>
void sol_log_compute_units_();
sol_log_pubkey
Writes a log entry with a Base58-encoded Solana public key.
The log message uses format Program log: %s
#include <sol/pubkey.h>
void sol_log_pubkey(/* r1 */ const SubPubkey *);
sol_create_program_address
Calculates a program-derived address (PDA) from the given program ID and seed list.
If the resulting address is a valid PDA (i.e. does not lie on Curve25519), returns 0
and writes the address to the pointer in r4. Otherwise returns 1
without writing to memory.
The caller is expected to provide a seeds input such that the PDA derivation succeeds. This makes sol_create_program_address
mostly useful for verifying PDA derivations. If you want to find new PDAs, consider using sol_try_find_program_address
instead.
See solana_program::pubkey::Pubkey::create_program_address
.
#include <sol/pubkey.h>
typedef struct {
const uint8_t *addr;
uint64_t len;
} SolSignerSeed;
uint64_t sol_create_program_address(
/* r1 */ const SolSignerSeed *seeds,
/* r2 */ int seeds_len,
/* r3 */ const SolPubkey *program_id,
/* r4 */ SolPubkey *program_address);
sol_try_find_program_address
Calculates a program-derived address (PDA) and bump seed from the given program ID and seed list. Since not all inputs to sol_create_program_address
result in successful derivations, the bump seed is used a one-byte-sized nonce that is appended to the seed list to search the input space for the next valid derivation. The search starts with bump seed 0
and ends at 255
, choosing the first value that results in a valid PDA. Each iteration writes the derived address to the pointer in r4 and the bump seed byte to the pointer in r5.
Returns 0
on success. In the unlikely case that the search fails, 1
is returned. In the highly unlikely failure case, bump seed 255
along with an (invalid) PDA gets written out to memory.
See solana_program::pubkey::Pubkey::try_find_program_address
.
#include <sol/pubkey.h>
typedef struct {
const uint8_t *addr;
uint64_t len;
} SolSignerSeed;
uint64_t sol_try_find_programs_address(
/* r1 */ const SolSignerSeed *seeds,
/* r2 */ int seeds_len,
/* r3 */ const SolPubkey *program_id,
/* r4 */ SolPubkey *program_address,
/* r5 */ uint8_t *bump_seed);
sol_sha256
Calculates the SHA-256 hash for the given byte inputs. The byte slices pointed to by r1 each get hashed as if they were a contiguous array. r2 specifies the number of byte slices. It is permitted to hash overlapping byte slices.
The default allowed max allowed slices count per operation is 20000. The compute unit costs are made up from two components:
sha256_base_cost
(default10
) once per syscall invocation.- Per slice,
sha256_byte_cost
times the number of two-byte pairs per slice or minimallymem_op_base_cost
for very short slices.
#include <sol/sha.h>
typedef struct {
const uint8_t *addr;
uint64_t len;
} SolBytes;
void sol_sha256(/* r1 */ const SolBytes *bytes,
/* r2 */ int bytes_len,
/* r3 */ uint8_t *result);
sol_keccak256
Calculates the Keccak-256 hash for the given byte inputs. Keccak-256 is similar to NIST SHA3-256 but preserves the original padding from the NIST competition submission, not the FIPS 202 variant. This version is compatible with the Ethereum protocols.
The function signature and compute unit costs are identical to sol_sha256
.
#include <sol/keccak.h>
typedef struct {
const uint8_t *addr;
uint64_t len;
} SolBytes;
void sol_keccak256(/* r1 */ const SolBytes *bytes,
/* r2 */ int bytes_len,
/* r3 */ uint8_t *result);
sol_secp256k1_recover
Recovers a secp256k1 public key from a signed message. Writes the uncompressed public key (64 bytes) to the pointer in r4. Compatible with Bitcoin and Ethereum.
Return codes:
0x00
: Success0x01
: Invalid hash provided0x02
: Invalid signature provided
#include <sol/secp256k1.h>
uint64_t sol_secp256k1_recover(/* r1 */ const uint8_t *hash,
/* r2 */ uint64_t recovery_id,
/* r3 */ const uint8_t *signature,
/* r4 */ uint8_t *result);
sol_blake3
Calculates the BLAKE3 hash for the given byte inputs.
The function signature and compute unit costs are identical to sol_sha256
.
#include <sol/blake3.h>
typedef struct {
const uint8_t *addr;
uint64_t len;
} SolBytes;
void sol_blake3(/* r1 */ const SolBytes *bytes,
/* r2 */ int bytes_len,
/* r3 */ uint8_t *result);
sol_curve_validate_point
Validates an elliptic curve point. Returns 0
if the point is valid, otherwise 1
.
Supported elliptic curves (specified in r1):
0x00
: CURVE25519_EDWARDS0x01
: CURVE25519_RISTRETTO
uint64_t sol_curve_validate_point(
/* r1 */ int curve_id,
/* r2 */ const void *point);
sol_curve_group_op
Provides elliptic curve group operations. Returns 0
on success, otherwise 1
.
See sol_curve_validate_point
for supported curves.
Supported group operations (specified in r2):
0x00
: Addition0x01
: Subtraction0x02
: Multiplication
r3 and r4 point to the left and right input curve points. If the operation was successful, the resulting point gets written to the pointer in r5.
uint64_t sol_curve_group_op(
/* r1 */ int curve_id,
/* r2 */ int group_op,
/* r3 */ const void *left_point,
/* r4 */ const void *right_point,
/* r5 */ void *result_point);
sol_get_clock_sysvar
Writes the clock sysvar to the pointer in r1.
typedef struct {
uint64_t slot;
int64_t epoch_start_timestamp;
uint64_t epoch;
uint64_t leader_schedule_epoch;
int64_t unix_timestamp;
} SysvarClock;
void sol_get_clock_sysvar(/* r1 */ SysvarClock *);
sol_get_epoch_schedule_sysvar
Writes the epoch schedule sysvar to the pointer in r1.
typedef struct {
uint64_t slots_per_epoch;
uint64_t leader_schedule_slot_offset;
bool warmup; /* one byte */
uint64_t first_normal_epoch;
uint64_t first_normal_slot;
} SysvarEpochSchedule;
void sol_get_epoch_schedule_sysvar(/* r1 */ SysvarEpochSchedule *);
sol_get_fees_sysvar
Writes the fees sysvar to the pointer in r1.
typedef struct {
uint64_t lamports_per_signature;
} SysvarFees;
void sol_get_fees_sysvar(/* r1 */ SysvarFees *);
sol_get_rent_sysvar
Writes the rent sysvar to the pointer in r1.
typedef struct {
uint64_t lamports_per_byte_year;
double exemption_threshold;
uint8_t burn_percent;
} SysvarRent;
void sol_get_rent_sysvar(/* r1 */ SysvarRent *);
sol_memcpy_
See memcpy(3)
.
Throws the SyscallError::CopyOverlapping
runtime error if dest and src overlap.
void sol_memcpy_(/* r1 */ void *restrict dest,
/* r2 */ const void *restrict src,
/* r3 */ size_t n);
sol_memmove_
See memmove(3)
.
void sol_memmove_(/* r1 */ void *dest,
/* r2 */ const void *src,
/* r3 */ size_t n);
sol_memcmp_
See memcmp(3)
.
int sol_memcmp_(/* r1 */ const void *s1,
/* r2 */ const void *s2,
/* r3 */ size_t n);
sol_memset_
See memset(3)
.
void *sol_memset_(/* r1 */ void *s,
/* r2 */ int c,
/* r3 */ size_t n);
sol_invoke_signed_c
Executes a cross-program invocation (CPI) given a Sealevel instruction, account infos and a list of seed list to derive signers. Each seed list determines one PDA to set as a signer.
Input r1
points to the instruction that will execute the CPI.
Input r2
points to the account infos array, r3
specifies its length.
Input r4
points to the array of seed lists, r5
specifies its length.
Returns with r0
set to zero.
#include <sol/cpi.h>
typedef struct {
SolPubkey *pubkey;
bool is_writable;
bool is_signer;
} SolAccountMeta;
typedef struct {
SolPubkey *program_id;
SolAccountMeta *accounts;
uint64_t account_len;
uint8_t *data;
uint64_t data_len;
} SolInstruction;
typedef struct {
SolPubkey *key;
uint64_t *lamports;
uint64_t data_len;
uint8_t *data;
SolPubkey *owner;
uint64_t rent_epoch;
bool is_signer;
bool is_writable;
bool executable;
} SolAccountInfo;
typedef struct {
const uint8_t *seed;
uint64_t seed_len;
} SolSignerSeed;
typedef struct {
const SolSignerSeed *seeds;
uint64_t num_seeds;
} SolSignerSeeds;
void sol_invoke_signed_c(
/* r1 */ const SolInstruction *instruction,
/* r2 */ const SolAccountInfo *account_infos,
/* r3 */ int num_account_infos,
/* r4 */ const SolSignerSeeds *signers_seeds,
/* r5 */ int num_signers_seeds);
sol_invoke_signed_rust
Identical effects to sol_invoke_signed_c
but using the Rust ABI for input parameters. This is the most complex syscall and scheduled to be removed soon. Please refer to the PR below for the reference of this syscall.
We also fixed some portability/soundness issues with the host implementation of this syscall: solana-labs/solana#26531
sol_alloc_free_
Provides memory allocation primitives backed by a bump allocator.
If r2
is a null pointer, allocates a chunk of memory of at least the size in r1
. Returns a valid pointer on success or a null pointer on failure.
If r2
is a valid pointer, does nothing.
void *sol_alloc_free_(/* r1 */ uint64_t size,
/* r2 */ void *free_addr);
sol_set_return_data
Sets the return data of the current instruction. This data can be retrieved with sol_get_return_data
in the parent instruction's context.
Copies the return data from the pointer in r1
with data len given in r2
into the return data buffer.
#include <sol/return_data.h>
void sol_set_return_data(/* r1 */ const void *addr,
/* r2 */ uint64_t len);
sol_get_return_data
Retrieves the return data of the CPI that has last returned back to the current context.
Copies return data into memory at r1
with max data len given in r2
. Writes the program ID that set the return data into r3
. Returns the number of bytes read, or 0
if no return data was set.
#include <sol/return_data.h>
uint64_t sol_get_return_data(/* r1 */ void *return_addr,
/* r2 */ uint64_t max_len,
/* r3 */ SolPubkey *program_id);
sol_log_data
Writes a log entry containing Base64-encoded to the Sealevel logging facility. The log message uses format Program log: %s
. The byte slices pointed to by r1 each get encoded as if they were a contiguous array.
The compute unit costs are made up from three components:
syscall_base_cost
(default100
) once per syscall invocation.syscall_base_cost
(default100
) times the number of slices.- One cost unit per byte read in total.
#include <sol/log.h>
void sol_log_data(/* r1 */ SolBytes *slices,
/* r2 */ uint64_t num_slices);
sol_get_processed_sibling_instruction
Copies data of a processed sibling Sealevel instruction to memory. For transaction-level instructions, the list of sibling instructions are the programs that have been invoked previously in the same transaction. Otherwise, it is the list of CPIs that the parent instruction has executed.
r1
specifies the instruction index in the list.r2
points to a descriptor of the inputs of the target instruction. As an input param, it specifies the expected instruction data length and number of accounts. After syscall execution, the fields are set to the actual values.r3
is filled in with the program ID invoked by the sibling instruction.- If the specified data length in
r2
matches the actual invocation, copies the instruction bytes tor4
and the accounts meta list tor5
.
typedef struct {
uint64_t data_len;
uint64_t num_accounts;
} SolProcessedSiblingInstruction;
typedef struct {
SolPubkey pubkey;
bool is_signer; /* one byte */
bool is_writable; /* one byte */
} SolAccountMeta;
bool sol_get_processed_sibling_instruction(
/* r1 */ int index,
/* r2 */ SolProcessedSiblingInstruction *meta,
/* r3 */ SolPubkey *program_id,
/* r4 */ void *data,
/* r5 */ SolAccountMeta *accounts);
sol_get_stack_height
Returns the height of the Sealevel invocation stack, which is 1
at transaction level and increases for every cross-program invocation. Note that this is unrelated to the SBF call stack.
uint64_t sol_get_stack_height();