Learn INK with Real Code Examples
Updated Nov 25, 2025
Code Sample Descriptions
1
Ink! Simple Counter Contract
#[ink::contract]
mod counter {
#[ink(storage)]
pub struct Counter {
value: u64,
}
impl Counter {
#[ink(constructor)]
pub fn new() -> Self {
Self { value: 0 }
}
#[ink(message)]
pub fn increment(&mut self) {
self.value += 1;
}
#[ink(message)]
pub fn reset(&mut self) {
self.value = 0;
}
}
}
A minimal Ink! contract defining a counter with increment and reset functions.
2
Ink! Owner-Only Message
#[ink::contract]
mod owner_message {
#[ink(storage)]
pub struct OwnerMessage {
owner: AccountId,
message: ink::prelude::string::String,
}
impl OwnerMessage {
#[ink(constructor)]
pub fn new() -> Self {
Self {
owner: Self::env().caller(),
message: String::from(""),
}
}
#[ink(message)]
pub fn set_message(&mut self, msg: String) {
assert!(self.env().caller() == self.owner);
self.message = msg;
}
}
}
A contract where only the owner can update a stored message.
3
Ink! Simple ERC20 Token
#[ink::contract]
mod token {
#[ink(storage)]
pub struct Token {
total: u64,
balances: ink::storage::Mapping<AccountId, u64>,
}
impl Token {
#[ink(constructor)]
pub fn new(amount: u64) -> Self {
let caller = Self::env().caller();
let mut balances = ink::storage::Mapping::new();
balances.insert(caller, &amount);
Self { total: amount, balances }
}
#[ink(message)]
pub fn transfer(&mut self, to: AccountId, amount: u64) {
let caller = self.env().caller();
let caller_balance = self.balances.get(caller).unwrap_or(0);
assert!(caller_balance >= amount);
self.balances.insert(caller, &(caller_balance - amount));
let to_balance = self.balances.get(to).unwrap_or(0);
self.balances.insert(to, &(to_balance + amount));
}
}
}
A basic ERC20-like token implementation in Ink! supporting mint, transfer, and balance queries.
4
Ink! Simple Vault (Deposit + Withdraw)
#[ink::contract]
mod vault {
#[ink(storage)]
pub struct Vault {}
impl Vault {
#[ink(constructor)]
pub fn new() -> Self { Self {} }
#[ink(message, payable)]
pub fn deposit(&mut self) {}
#[ink(message)]
pub fn withdraw(&mut self, amount: u128) {
self.env().transfer(self.env().caller(), amount).unwrap();
}
}
}
A vault contract for storing native chain tokens with deposit/withdraw logic.
5
Ink! Timelock Contract
#[ink::contract]
mod timelock {
#[ink(storage)]
pub struct Timelock {
unlock: u64,
}
impl Timelock {
#[ink(constructor)]
pub fn new(unlock: u64) -> Self { Self { unlock } }
#[ink(message)]
pub fn withdraw(&mut self) {
assert!(self.env().block_timestamp() >= self.unlock);
self.env().transfer(self.env().caller(), self.env().balance()).unwrap();
}
}
}
Funds can be withdrawn only after a specific block timestamp.
6
Ink! Simple Voting
#[ink::contract]
mod voting {
#[ink(storage)]
pub struct Voting {
yes: u64,
no: u64,
}
impl Voting {
#[ink(constructor)]
pub fn new() -> Self { Self { yes: 0, no: 0 } }
#[ink(message)]
pub fn vote(&mut self, choice: bool) {
if choice { self.yes += 1; } else { self.no += 1; }
}
}
}
A minimal yes/no voting system implemented in Ink!.
7
Ink! Whitelist Access
#[ink::contract]
mod whitelist {
#[ink(storage)]
pub struct Whitelist {
owner: AccountId,
allowed: ink::storage::Mapping<AccountId, bool>,
}
impl Whitelist {
#[ink(constructor)]
pub fn new() -> Self {
Self { owner: Self::env().caller(), allowed: ink::storage::Mapping::new() }
}
#[ink(message)]
pub fn add(&mut self, user: AccountId) {
assert!(self.env().caller() == self.owner);
self.allowed.insert(user, &true);
}
#[ink(message)]
pub fn restricted(&self) {
assert!(self.allowed.get(self.env().caller()).unwrap_or(false));
}
}
}
Only whitelisted users can perform a restricted action.
8
Ink! Event Logger
#[ink::contract]
mod logger {
#[ink(event)]
pub struct Log {
#[ink(topic)]
from: AccountId,
msg: String,
}
#[ink(storage)]
pub struct Logger {}
impl Logger {
#[ink(constructor)]
pub fn new() -> Self { Self {} }
#[ink(message)]
pub fn fire(&self, msg: String) {
self.env().emit_event(Log { from: self.env().caller(), msg });
}
}
}
Demonstrates Ink! events with a simple log message.
9
Ink! Immutable Config Example
#[ink::contract]
mod config {
#[ink(storage)]
pub struct Config {
config: u64,
}
impl Config {
#[ink(constructor)]
pub fn new(num: u64) -> Self {
Self { config: num }
}
#[ink(message)]
pub fn get(&self) -> u64 { self.config }
}
}
Shows how to use immutable data in Ink!.
10
Ink! Simple Multiplier
#[ink::contract]
mod multiplier {
#[ink(storage)]
pub struct Multiplier {
factor: u64,
}
impl Multiplier {
#[ink(constructor)]
pub fn new(f: u64) -> Self {
Self { factor: f }
}
#[ink(message)]
pub fn multiply(&self, x: u64) -> u64 {
x * self.factor
}
}
}
Multiplies any input by a stored factor.