Best Practices
Guidance for writing secure, efficient, and maintainable ink! 5.1.1 contracts on BelizeChain.
Project Structure
my-project/
├── Cargo.toml # workspace manifest
├── contracts/
│ ├── token/ # one contract per directory
│ │ ├── Cargo.toml
│ │ └── lib.rs
│ └── marketplace/
│ ├── Cargo.toml
│ └── lib.rs
├── sdk/ # typed client
└── tests/ # integration tests
Do: one contract per directory, clear domain names, workspace inheritance for shared dependency versions.
Don't: mix contracts in one file, or use generic names like contract1.
Security Patterns
Access control
Use the access-control library contracts for Ownable / RBAC / Pausable rather than hand-rolling checks:
// Every admin path must validate the caller
let caller = self.env().caller();
self.ensure_owner(caller)?;
Arithmetic safety
Use saturating or checked operations everywhere — plain +/-/* can panic the contract:
let total = balance.saturating_add(amount);
let share = total.checked_div(shares).ok_or(Error::DivisionByZero)?;
Events
Emit events for every state change (Transfer, Approval, RoleGranted, …). Event-based indexing is how interfaces stay observable.
Storage Management
- Keep on-chain state minimal — large data belongs in Pakit with content IDs on-chain
- Use
MappingoverVecfor per-account data - Pack small values into single storage items to reduce rent
Gas Optimization
Measure before optimizing: run cargo contract build --release and compare the generated metadata and code sizes. Prefer fewer cross-contract calls and batch operations where the standard supports them (e.g. PSP37 batch mint).
Testing
- Unit tests —
#[ink::test]for message logic - Integration tests — E2E against a local node for the full upload/instantiate/call cycle
- Test failure paths: unauthorized callers, insufficient balance, paused state
Related
- Security Checklist — run before every deploy
- Quick Reference
- ink! documentation