Syscalls
Use this page when you need the low-level mutation and control operations the Thru VM exposes to Rust programs.
Every wrapper lives in thru_core::syscall and is compiled only for riscv64 targets. All of them except sys_exit are unsafe, because they let the VM write to memory your program may still be holding references to. Prefer the AccountManager method in the last column: it checks borrows at runtime first.
Return convention
Section titled “Return convention”Most wrappers return SyscallCode, an enum over the VM’s status values (Success = 0, SuccessExit = 1, errors negative). See Error Handling and Return Codes.
Two wrappers differ:
sys_invokereturns(SyscallCode, SyscallCode): the syscall-level result and the callee-level result.sys_increment_anonymous_segment_szreturns(SyscallCode, *mut u8): the result and the new segment address.sys_logandsys_emit_eventreturn the rawu64from the VM.sys_exitnever returns.
Account and balance syscalls
Section titled “Account and balance syscalls”| Wrapper | Signature | Safe equivalent |
|---|---|---|
sys_set_account_data_writable | (account_idx: u64) -> SyscallCode | mgr.set_account_data_writable(idx) |
sys_account_transfer | (from_idx: u64, to_idx: u64, amount: u64) -> SyscallCode | mgr.account_transfer(from, to, amount) |
sys_account_create | (account_idx: u64, seed: &[u8; SEED_SIZE], proof: *const u8, proof_sz: u64) -> SyscallCode | mgr.account_create(idx, seed, proof) |
sys_account_create_ephemeral | (account_idx: u64, seed: &[u8; SEED_SIZE]) -> SyscallCode | mgr.account_create_ephemeral(idx, seed) |
sys_account_create_eoa | (account_idx: u64, signature: &Signature, proof: *const u8, proof_sz: u64) -> SyscallCode | mgr.account_create_eoa(idx, signature, proof) |
sys_account_delete | (account_idx: u64, signature: Option<&Signature>) -> SyscallCode | mgr.account_delete(idx) or mgr.account_delete_signed(idx, signature) |
sys_account_resize | (account_idx: u64, new_size: u64) -> SyscallCode | mgr.account_resize(idx, new_size) |
sys_account_compress | (account_idx: u64, proof: *const u8, proof_sz: u64) -> SyscallCode | mgr.account_compress(idx, proof) |
sys_account_decompress | (account_idx: u64, meta: *const u8, data: *const u8, proof: *const u8, proof_sz: u64) -> SyscallCode | mgr.account_decompress(idx, meta, data, proof) |
sys_account_set_flags | (account_idx: u16, flags: u8) -> SyscallCode | mgr.account_set_flags(idx, flags) |
The AccountManager variants take a &StateProof and pass its pointer and footprint for you, and they panic if the target account is currently borrowed. mgr.create_and_init(idx, seed, proof, data_size) combines create, make-writable, resize, and borrow into one call.
Account seeds are always SEED_SIZE (32) bytes.
Control and diagnostics syscalls
Section titled “Control and diagnostics syscalls”| Wrapper | Signature | Notes |
|---|---|---|
sys_invoke | (instr_data: *const u8, instr_data_sz: u64, program_account_idx: u16, auth: *const u8) -> (SyscallCode, SyscallCode) | Use mgr.invoke(...); see Cross-Program Invocation. |
sys_exit | (exit_code: u64, revert: u64) -> ! | Safe to call. Prefer program_utils::succeed / program_utils::revert. |
sys_log | (data: *const u8, data_sz: u64) -> u64 | Prefer tvm_println!. |
sys_emit_event | (data: *const u8, data_sz: u64) -> u64 | Emits an event payload for indexers and clients. |
Memory syscalls
Section titled “Memory syscalls”| Wrapper | Signature | Notes |
|---|---|---|
sys_set_anonymous_segment_sz | (addr: *mut u8) -> SyscallCode | Sizes an anonymous segment; used by the generated boot shim for the stack. |
sys_increment_anonymous_segment_sz | (ptr: *mut (), delta: u64) -> (SyscallCode, *mut u8) | Grows a segment. program_utils::grow_stack(delta) wraps this for the stack. |
Syscall numbers
Section titled “Syscall numbers”The syscall ids are public constants, useful when cross-referencing the VM syscalls specification:
| Constant | Value |
|---|---|
SET_ANONYMOUS_SEGMENT_SZ | 0x00 |
INCREMENT_ANONYMOUS_SEGMENT_SZ | 0x01 |
SET_ACCOUNT_DATA_WRITABLE | 0x02 |
ACCOUNT_TRANSFER | 0x03 |
ACCOUNT_CREATE | 0x04 |
ACCOUNT_CREATE_EPHEMERAL | 0x05 |
ACCOUNT_DELETE | 0x06 |
ACCOUNT_RESIZE | 0x07 |
ACCOUNT_COMPRESS | 0x08 |
ACCOUNT_DECOMPRESS | 0x09 |
INVOKE | 0x0A |
EXIT | 0x0B |
LOG | 0x0C |
EMIT_EVENT | 0x0D |
ACCOUNT_SET_FLAGS | 0x0E |
ACCOUNT_CREATE_EOA | 0x0F |
Segment addressing
Section titled “Segment addressing”VM addresses are seg_type << 40 | seg_idx << 24 | offset. thru_core::mem exposes the pieces:
use thru_core::mem::{compute_segment_addr, vm_ptr, SEG_TYPE_ACCOUNT_DATA};
let addr = compute_segment_addr(SEG_TYPE_ACCOUNT_DATA, account_idx as usize, 0);let ptr: *const u8 = vm_ptr(SEG_TYPE_ACCOUNT_DATA, account_idx as usize, 0);| Segment type | Value |
|---|---|
SEG_TYPE_READONLY_DATA | 0x00 |
SEG_TYPE_ACCOUNT_METADATA | 0x02 |
SEG_TYPE_ACCOUNT_DATA | 0x03 |
SEG_TYPE_STACK | 0x05 |
SEG_TYPE_HEAP | 0x07 |
Read-only data segments are indexed by SEG_IDX_TXN_DATA (0x0001), SEG_IDX_SHADOW_STACK (0x0002), SEG_IDX_PROGRAM (0x0003), and SEG_IDX_BLOCK_CTX (0x0004).
For pointer work that must keep VM provenance, use VmPtr<T> / VmPtrMut<T> and the helpers copy_nonoverlapping, copy_bytes_strict, and memset from thru_core::vmptr instead of raw core::ptr calls.