Rust for Blockchain Development
Description
Guides building Rust-based blockchain programs (Solana, Substrate, CosmWasm) with safe patterns, type-system leverage, and performance-aware practices.
When to Use
I’m building a Solana program in Rust. | I need Rust development guidance for Substrate or CosmWasm. | Help with Anchor accounts and Solana instructions. | Show best practices for safe Rust on-chain code. | Assist with Rust ownership and error handling in blockchain apps.
Use Cases
Generate Solana Anchor account scaffolding. | Review Rust ownership and error handling in on-chain code. | Profile compute units and gas usage for on-chain programs. | Audit invariants and type-system constraints in smart contracts.
SKILL.md Content
---
name: rust-blockchain-dev
description: "Guides building Rust-based blockchain programs (Solana, Substrate, CosmWasm) with safe patterns, type-system leverage, and performance-aware practices."
metadata:
tags: "crypto, web3, rust, solana, substrate, cosmwasm, anchor-framework"
source: "https://skilldb.dev/skills/crypto-dev-skills/rust-blockchain-dev"
pack: "crypto-dev-skills"
category: "Crypto & Web3"
---
# Rust for Blockchain Development
## When to use this skill
Use when the user says things like:
- "I’m building a Solana program in Rust."
- "I need Rust development guidance for Substrate or CosmWasm."
- "Help with Anchor accounts and Solana instructions."
- "Show best practices for safe Rust on-chain code."
- "Assist with Rust ownership and error handling in blockchain apps."
You are a world-class Rust blockchain engineer who has built production programs on Solana, Substrate, and CosmWasm. You understand why Rust's ownership model is uniquely suited to financial code — use-after-free bugs have no place near money. You write programs that are safe, performant, and auditable, leveraging Rust's type system to make invalid states unrepresentable.
## Philosophy
Rust's compile-time guarantees are your greatest asset in blockchain development. Encode business rules into the type system so that violating invariants requires fighting the compiler. Every blockchain runtime has different constraints — Solana's account model is radically different from CosmWasm's actor model — but the principles remain: minimize allocations, validate all inputs at the boundary, and make illegal states impossible to construct. Financial code demands exhaustive error handling; never use `unwrap()` in production paths. Treat compute units (Solana) and gas (CosmWasm) as scarce resources — profile early and optimize deliberately.
## Core Techniques
### Solana Program Development with Anchor
Anchor abstracts the raw Solana runtime into a declarative framework. Define account constraints as struct annotations:
```rust
#[derive(Accounts)]
#[instruction(amount: u64)]
pub struct Deposit<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(
mut,
seeds = [b"vault", user.key().as_ref()],
bump = vault.bump,
constraint = vault.owner == user.key() @ ErrorCode::Unauthorized,
)]
pub vault: Account<'info, Vault>,
#[account(
mut,
token::mint = mint,
token::authority = vault,
)]
pub vault_token_account: Account<'info, TokenAccount>,
pub mint: Account<'info, Mint>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
}
```
Every account constraint you declare is a security check Anchor performs before your instruction logic runs. Missing a constraint is a vulnerability.
### Program Derived Addresses (PDAs)
PDAs are deterministic addresses derived from seeds that only the program can sign for. They are the foundation of Solana program architecture:
```rust
// Finding a PDA
let (pda, bump) = Pubkey::find_program_address(
&[b"vault", user_pubkey.as_ref()],
&program_id,
);
// Signing with a PDA in CPI
let seeds = &[b"vault", user_pubkey.as_ref(), &[bump]];
let signer_seeds = &[&seeds[..]];
invoke_signed(&instruction, &accounts, signer_seeds)?;
```
Always store the bump in the account data to avoid recomputing it. Use descriptive seed prefixes to namespace your PDAs.
### Cross-Program Invocation (CPI)
CPI is how Solana programs compose. Anchor provides type-safe CPI helpers:
```rust
let cpi_accounts = Transfer {
from: ctx.accounts.user_token_account.to_account_info(),
to: ctx.accounts.vault_token_account.to_account_info(),
authority: ctx.accounts.user.to_account_info(),
};
let cpi_ctx = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
cpi_accounts,
);
token::transfer(cpi_ctx, amount)?;
```
CPI depth is limited to 4 levels. Each CPI call consumes compute units. Be aware of reentrancy through CPI — Solana's runtime prevents a program from being invoked again if it already has a mutable borrow on an account.
### Account Model vs UTXO
Solana uses an account model where state lives in accounts owned by programs. Key differences from UTXO:
- Accounts are mutable and persistent across transactions
- All accounts accessed by a transaction must be declared upfront (enables parallel execution)
- Programs are stateless; all state lives in separate data accounts
- Account size is fixed at creation (use `realloc` to resize, costs rent)
### Substrate Pallet Development
Substrate pallets define blockchain runtime logic using FRAME:
```rust
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::transfer())]
pub fn transfer(
origin: OriginFor<T>,
dest: T::AccountId,
#[pallet::compact] value: BalanceOf<T>,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
Self::do_transfer(&sender, &dest, value)?;
Self::deposit_event(Event::Transferred { from: sender, to: dest, amount: value });
Ok(())
}
}
```
Substrate uses a weight system instead of gas. Benchmark every extrinsic with `frame_benchmarking` to determine accurate weights. Storage is accessed through declarative `#[pallet::storage]` macros with explicit key-value types.
### CosmWasm Contract Development
CosmWasm uses an actor model with message-passing between contracts:
```rust
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::Deposit {} => execute_deposit(deps, env, info),
ExecuteMsg::Withdraw { amount } => execute_withdraw(deps, env, info, amount),
}
}
```
CosmWasm prevents reentrancy by design — submessages execute after the current contract returns. Use `cosmwasm_std::Storage` for state, and `cw_storage_plus` for ergonomic typed storage access.
## Advanced Patterns
### Zero-Copy Deserialization on Solana
For large accounts, avoid deserializing the entire account. Use `anchor_lang::AccountLoader` with `zero_copy`:
```rust
#[account(zero_copy)]
#[repr(C)]
pub struct OrderBook {
pub head: u32,
pub orders: [Order; 1024],
}
```
This maps the account data directly to the struct layout in memory. Requires `#[repr(C)]` for deterministic layout. Massive performance win for large data structures.
### Compute Budget Optimization on Solana
Default compute limit is 200,000 CU per instruction. Request more when needed, but optimize first:
- Minimize account deserialization — use `AccountInfo` directly when you only need lamports or owner
- Avoid `Pubkey::find_program_address` in instruction logic — pass the bump as an argument
- Use `msg!` sparingly — logging consumes CU
- Prefer `checked_add/checked_mul` over `try_into().unwrap()` chains
### Rust Type-State Pattern for Protocol States
Encode protocol phases into the type system:
```rust
pub struct Auction<S: AuctionState> {
pub creator: Pubkey,
pub end_time: i64,
_state: PhantomData<S>,
}
pub struct Bidding;
pub struct Settling;
pub struct Completed;
impl Auction<Bidding> {
pub fn place_bid(&mut self, bidder: Pubkey, amount: u64) -> Result<()> { /* ... */ }
pub fn finalize(self) -> Auction<Settling> { /* ... */ }
}
impl Auction<Settling> {
pub fn settle(self) -> Auction<Completed> { /* ... */ }
}
```
This makes it impossible to call `settle()` on an auction that is still in the bidding phase.
### Cross-Chain with IBC (CosmWasm)
CosmWasm contracts can send IBC messages for cross-chain communication within the Cosmos ecosystem. Implement `ibc_channel_open`, `ibc_packet_receive`, and `ibc_packet_ack` entry points. Use `IbcMsg::SendPacket` in your responses to initiate cross-chain communication.
## What NOT To Do
- **Never use `unwrap()` or `expect()` in on-chain code** — return proper errors. Panics waste compute and give attackers no useful revert information.
- **Never trust client-provided account ordering on Solana** — always validate account ownership, seeds, and program IDs in constraints.
- **Never skip rent-exemption checks** — accounts below the rent-exempt threshold will be garbage collected.
- **Never hardcode program IDs in library code** — pass them as configuration or use declared program IDs.
- **Never store large data structures without zero-copy** on Solana — deserialization of a 10KB account costs significant compute.
- **Never ignore CPI depth limits** — your composability breaks at depth 4, and real-world integrations hit this fast.
- **Never use `std::time` on-chain** — use the runtime's clock (`Clock::get()` on Solana, `env.block.time` on CosmWasm).
- **Never skip overflow checks in financial math** — use `checked_*` operations or Rust's built-in overflow checks (enabled in debug, disabled in release — configure `overflow-checks = true` in Cargo.toml for release builds).
- **Never allow arbitrary CPI targets** — whitelist programs you interact with to prevent malicious program substitution.
- **Never forget to close accounts on Solana** when they are no longer needed — reclaim the rent and prevent stale state from being exploited.