More About the On-Chain Program
To write an on-chain Solana program, follow these steps:
1. Implement the Entrypoint Function
Provide a function whose type signature matches solana_program::entrypoint:
#![allow(unused)]
fn main() {
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult
}
program_id— the program’s pubkey (can be changed usingsolana-keygen grindto generate a vanity keypair, passed as--program-idduring deployment).accounts— consolidated list of all accounts that instructions read and/or write to. They appear in the orderAccountMetastructs are added to each instruction and the order instructions are added to the containing message. AllAccountInfoentries must have their writable/readable/signer attributes set. For example, the counter account is constructed withAccountMeta::new— writable but not a signer.instruction_data— raw byte array of data passed from the client.
2. Decorate with the Entrypoint Macro
Apply the entrypoint macro:
#![allow(unused)]
fn main() {
entrypoint!(process_instruction);
}
This embeds your implementation inside an external C function called entrypoint (annotated with no_mangle so the compiler keeps the name).
3. Define no-entrypoint Feature
During development you may depend on other crates that have their own entrypoints. Since there cannot be multiple entrypoints at runtime, declare the entrypoint feature in program/Cargo.toml:
[features]
no-entrypoint = []
If another crate wants to use your program’s types without including the entrypoint, they add:
program = { version = "0.1.0", path = "../program", features = ["no-entrypoint"] }
Read more in the Solana docs.
4. Build the Program
During cargo build-bpf, the program is compiled to Berkeley Packet Filter (BPF) bytecode and stored as an ELF shared object. A program keypair is also generated — its pubkey becomes the default program_id.
5. Deploy to the Network
The Solana CLI:
- Breaks the compiled bytecode into smaller chunks (due to restricted transaction size)
- Sends chunks to an intermediate on-chain buffer account in a series of transactions
- Once transmission is complete and verified, a final transaction moves the buffered content to the program’s data account
This completes a new deployment or a program upgrade. Transaction costs are deducted from the payer’s account.
See this post for more detail.
State Management
Solana on-chain programs are stateless and compiled as shared libraries (crate-type = ["cdylib"]). They cannot maintain state across invocations. To persist state, programs use accounts that they own.
There is a limit of ~10 MB per account. Space incurs cost (rent). An account can be rent-exempt if it maintains at least two years’ worth of rent as balance. On-chain programs are expected to be rent-exempt.
Calculate rent-exempt lamports:
solana rent 1000 # bytes
Or programmatically via RpcClient::get_minimum_balance_for_rent_exemption.