Accounts
Every piece of data that is stored on the Thru network lives inside an account. The purpose of the blockchain is to keep the state of all accounts up to date.
What is an Account?
Section titled “What is an Account?”An account is a container for data that has:
- An address: Also known as its public key (or pubkey for short), this is a unique 32-byte identifier for the account.
- Data: The actual bytes stored in the account, which is allowed to be up to 16 MiB.
- Metadata: Information about the account’s state and properties. In the C SDK, account metadata is exposed as:
/* From sdks/c/thru-sdk/c/tn_sdk_txn.h */struct __attribute__(( packed )) tsdk_account_meta { uchar version; /* Metadata format version */ uchar flags; /* Lifecycle/type flags (program, ephemeral, deleted, compressed, etc.) */ uint data_sz; /* Account data size in bytes (up to 16 MiB) */ ulong seq; /* Sequence counter incremented when account state changes */ tn_pubkey_t owner; /* Program that is allowed to modify this account's data */ ulong balance; /* Native-token balance held by this account */ ulong nonce; /* Account nonce used by transaction/signing flows */};Common flag values are defined in sdks/c/thru-sdk/c/tn_sdk.h.
Conceptually, you can think of the Thru network as one giant map from 32-byte addresses to sequences of bytes.
Accounts and Programs
Section titled “Accounts and Programs”Accounts are only useful to us if they can be read and manipulated. Programs, or smart contracts, allow accounts to be updated. The logic of how accounts should be updated is written in code.
Programs are themselves accounts, and their compiled code is stored in their account data. When executing, they are allowed to read from any account, and can write to the accounts that they own.
For more information, see Programs.
Addresses
Section titled “Addresses”Every account is identified by a 32-byte address, which is a public key. There are two ways accounts get their addresses:
- Keypair-derived accounts: You can think of these as being accounts that can be logged into. They are generated from a cryptographic keypair, which consists of a private and public key, where anyone with the private key can prove their identity, which can be verified with the public key.
- Program-derived addresses (PDAs): These are addresses derived from a program’s address and a seed. These accounts have no private key. The owning program can authorize how they are used during invocation, and can mutate their data.
Ownership
Section titled “Ownership”Every account is owned by exactly one program, which is the program that created it. The owning program has exclusive write access to the account’s data. Other programs can read the account, but only the owner can modify it.
Note that for keypair-derived accounts, the owner of the account is the system program.
The data for an account is just a byte array. Programs interpret these bytes however they want; there is not an enforced schema. Common patterns include:
- Fixed-size structs: Store a C struct directly in the account
- Variable-size data: Use a header that indicates data layout
- Nested accounts: Store references (addresses) to other accounts
/* Example: A simple account data structure */struct user_profile { tn_pubkey_t owner; /* 32 bytes */ ulong balance; /* 8 bytes */ uchar username[32]; /* 32 bytes */};/* Total: 72 bytes */When you create an account, you specify its size. To store the user_profile struct above, you’d create an account with at least 72 bytes. Accounts can be resized later, but this requires the owning program’s permission.
Account Lifecycle
Section titled “Account Lifecycle”Creation
Section titled “Creation”New accounts are created by programs using state proofs. A creation proof cryptographically demonstrates that an address is available—no account currently exists at that address.
/* Creating a new account with a creation proof */tsys_account_create( account_index, seed, proof_data, proof_size );Once created, an account exists on the blockchain and can be read or modified by programs.
Modification
Section titled “Modification”The owning program can modify an account’s data at any time. Before writing to an account, the program must mark it as writable within the current transaction:
/* Make account data writable for this transaction */tsys_set_account_data_writable( account_index );
/* Now we can write to account data */struct counter_account { ulong count;};struct counter_account * account = (struct counter_account *)tsdk_get_account_data_ptr( (ushort)account_index );account->count += 1UL;Compression
Section titled “Compression”To save on-chain storage, accounts can be compressed. A compressed account’s data is removed from active validator storage and stored off-chain. The account still exists—its current state is committed to the blockchain through a Merkle tree—but it cannot be accessed until it’s decompressed.
Compressing and decompressing accounts requires state proofs to maintain security guarantees.
Deletion
Section titled “Deletion”Programs can delete accounts they own. This removes the account from the blockchain entirely and frees up its storage.
/* Delete an account */tsys_account_delete( account_index );Compressed vs. Uncompressed Accounts
Section titled “Compressed vs. Uncompressed Accounts”The Thru network distinguishes between compressed and uncompressed accounts:
Uncompressed accounts are active and stored in validator memory. They can be accessed and modified immediately by any transaction.
Compressed accounts are archived off-chain. Their state is cryptographically committed to the blockchain via a Merkle tree, but validators don’t store the full data. To use a compressed account, you must first decompress it by providing a state proof.
Compression dramatically reduces storage costs for accounts that are accessed infrequently. For example, a user might compress their account when they’re not actively using the network, then decompress it months later when they want to make a transaction.
See Account Compression and State Proofs for technical details on how compression works.
Accounts in Transactions
Section titled “Accounts in Transactions”When you submit a transaction, you specify which accounts it will access. Each account is passed to the program by index:
/* Instruction data includes the account index */struct instruction_args { ushort account_index; /* Which account to operate on */ /* ... other instruction data ... */};Programs access accounts through these indices. The runtime ensures that programs can only write to accounts they’re permitted to modify.
Working with Accounts in Programs
Section titled “Working with Accounts in Programs”Here’s a simple example of creating and using an account in a program:
#include <thru-sdk/c/tn_sdk.h>#include <thru-sdk/c/tn_sdk_syscall.h>
/* Account data structure */struct counter_account { ulong count;};
TSDK_ENTRYPOINT_FN voidstart( void ) { tsdk_txn_t const * txn = tsdk_get_txn( ); uchar const * instruction_data = tsdk_txn_get_instr_data( txn ); ulong instruction_data_sz = tsdk_txn_get_instr_data_sz( txn );
/* Parse instruction data (account index and creation proof) */ ushort account_index = /* extracted from instruction_data */; uchar seed[TN_SEED_SIZE] = /* extracted from instruction_data */; uchar const * proof_data = /* extracted from instruction_data */; ulong proof_size = /* calculated from proof header */;
/* Create the account */ tsys_account_create( account_index, seed, proof_data, proof_size );
/* Make it writable and set size */ tsys_set_account_data_writable( account_index ); tsys_account_resize( account_index, sizeof(struct counter_account) );
/* Initialize the data */ struct counter_account * account = tsdk_get_account_data_ptr( account_index ); account->count = 0;
tsdk_return( TSDK_SUCCESS );}Later transactions can read and modify the account’s count field, implementing a simple counter. For a more complete example, see Quickstart: Build a C Program.
Key Takeaways
Section titled “Key Takeaways”- Accounts are containers for data identified by 32-byte addresses
- Programs own accounts and modify them
- Account data is unstructured — programs define their own data layouts
- Accounts can be compressed to reduce storage costs when not actively used
- Transactions specify which accounts they access and programs use indices to reference them
Understanding these fundamentals will help you design efficient programs and manage state effectively on the Thru network.
Next Steps
Section titled “Next Steps”- Learn how to build a C program that creates and uses accounts
- Explore account compression for advanced state management
- Review the syscalls reference for account manipulation functions