---
title: Program Structure
description: Understand the minimal shape of a Thru C program and the control
  flow an agent should expect.
source_url:
  html: https://thru.org/docs/sdks/c-reference/program-structure/
  md: https://thru.org/docs/sdks/c-reference/program-structure.md
---

# Program Structure

Use this page when you need the smallest reliable mental model for how a C program starts, reads input, mutates state, and exits.

## Minimal program shape

```c
#include <thru-sdk/c/tn_sdk.h>

TSDK_ENTRYPOINT_FN void
start( void ) {
  tsdk_txn_t const * txn = tsdk_get_txn( );

  if( tsdk_txn_get_instr_data_sz( txn ) == 0UL ) {
    tsdk_revert( 1UL );
  }

  tsdk_return( TSDK_SUCCESS );
}
```

## Execution model

| Step | What normally happens |
| - | - |
| Enter | The VM jumps to a function marked with `TSDK_ENTRYPOINT_FN`. |
| Read inputs | The program reads transaction and account context through helpers like `tsdk_get_txn`, `tsdk_get_account_meta`, and `tsdk_get_account_data_ptr`. |
| Decode | Instruction bytes are read from the transaction body, usually through `tsdk_txn_get_instr_data` and `tsdk_txn_get_instr_data_sz`. |
| Validate | Programs check instruction size, account indices, ownership, authorization, and any variable-length payload boundaries before mutating state. |
| Mutate or invoke | Programs call syscalls such as `tsys_account_resize`, `tsys_account_transfer`, or `tsys_invoke`. |
| Exit | Programs terminate with `tsdk_return(...)` for success or `tsdk_revert(...)` for failure. |

## Recommended file split

| File | Purpose |
| - | - |
| `your_program.h` | Instruction tags, error codes, packed payload structs, account-data structs, and local constants. |
| `your_program.c` | Entry point, validation, instruction dispatch, syscall usage, and local helper functions. |

This split keeps payload layout definitions easy to reuse without forcing an agent to reread the whole implementation file.

## Typical instruction dispatch pattern

```c
#include <thru-sdk/c/tn_sdk.h>

typedef struct __attribute__(( packed )) {
  uint instruction_type;
} my_instr_hdr_t;

TSDK_ENTRYPOINT_FN void
start( void ) {
  tsdk_txn_t const * txn = tsdk_get_txn( );
  uchar const *      instr = tsdk_txn_get_instr_data( txn );
  ulong              instr_sz = tsdk_txn_get_instr_data_sz( txn );

  if( instr_sz < sizeof( my_instr_hdr_t ) ) tsdk_revert( 0x1000UL );

  my_instr_hdr_t const * hdr = (my_instr_hdr_t const *)instr;

  switch( hdr->instruction_type ) {
  case 0U:
    /* handle create */
    break;
  case 1U:
    /* handle update */
    break;
  default:
    tsdk_revert( 0x1001UL );
  }

  tsdk_return( TSDK_SUCCESS );
}
```

## Control-flow rules

- `tsdk_return` does not come back.
- `tsdk_revert` does not come back.
- `tsys_exit` underlies both helpers and is also terminal.
- If you need cleanup, do it before calling either exit helper.

## Common structure choices

| Goal | Common choice |
| - | - |
| Small fixed instruction | One packed struct with a leading tag. |
| Variable-length instruction | Fixed header plus byte tail, with explicit size checks before casting or copying. |
| Stateful program | One or more packed account-data structs plus helper functions to read and write them. |
| CPI-heavy program | Entry point plus small helpers to build invoke payloads and auth descriptors. |

## Notes

- Prefer `tsdk_get_txn()` as the root of most program reads.
- Prefer a local header for your instruction/account layouts so the implementation page can stay focused on logic.
- Keep the entrypoint thin: validate, dispatch, return.
