Skip to main content

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 Mapping over Vec for 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