Program Structure
Use this page when you need the smallest reliable mental model for how a Rust program starts, reads input, and exits.
Minimal program shape
Section titled “Minimal program shape”#![no_std]#![no_main]
use thru_sdk_macros::*;
#[entry(stack_size = 4096)]fn main(_instr_data: &[u8]) -> Result<u64, u64> { Ok(0)}Every program is #![no_std] and #![no_main]. thru-core installs the #[panic_handler], so do not define your own.
The #[entry] macro
Section titled “The #[entry] macro”#[entry] comes from thru-sdk-macros. It generates the _start boot shim that the VM jumps to, sets up the stack, and exports the start symbol that calls your function.
#[entry(stack_size = 8192)]| Argument | Rules |
|---|---|
stack_size | Stack size in bytes. Must be a multiple of 4096. Defaults to 4096 when omitted, but the argument list must not be empty. |
Accepted entry signatures
Section titled “Accepted entry signatures”The macro validates your function signature and fails to compile otherwise:
fn main(instr_data: &[u8]) -> Result<u64, u64>fn main(instr_data: &[u8], mgr: AccountManager<N>) -> Result<u64, u64>fn main(instr_data: &[u8]) -> !fn main(instr_data: &[u8], mgr: AccountManager<N>) -> !When the second parameter is present, the macro builds the AccountManager from the current transaction for you and exits with code 1 if it cannot be constructed.
Execution model
Section titled “Execution model”| Step | What happens |
|---|---|
| Enter | The VM jumps to the generated _start, which validates the transaction version, sizes the stack segment, and calls start. |
| Read inputs | start turns the instruction-data pointer and length into a &[u8] (empty when the pointer is null) and optionally constructs the AccountManager. |
| Run | Your entry function runs with that slice. |
| Exit | A returned Ok(code) exits with revert = 0; Err(code) exits with revert = 1. A !-returning entry must exit itself. |
Exiting explicitly
Section titled “Exiting explicitly”use thru_core::program_utils;
program_utils::succeed(0); /* exit, no revert */program_utils::revert(7); /* exit with revert */Both wrap the exit syscall and never return. See Error Handling and Return Codes.
Multi-instruction dispatch
Section titled “Multi-instruction dispatch”Programs with several instructions usually tag-dispatch on the first byte of the instruction data:
#![no_std]#![no_main]
use thru_core::*;use thru_sdk_macros::*;
#[entry(stack_size = 8192)]fn main(instr_data: &[u8], mgr: AccountManager<4>) -> Result<u64, u64> { let Some((tag, rest)) = instr_data.split_first() else { return Err(1); };
match tag { 0 => initialize(rest, &mgr), 1 => transfer(rest, &mgr), _ => Err(2), }}Logging
Section titled “Logging”use thru_core::tvm_println;
tvm_println!("balance is {}", balance);tvm_println!(bufsize = 128, "short message {}", idx);tvm_println! formats into a stack-allocated heapless::String (1024 bytes by default) and passes it to the log syscall.