Skip to main content
CodeSpeedTest
Languages
Start TypingJump into a test — pick any languageAdaptive TrainingUnlock chars as you master themPractice DrillsFocused sessions targeting weak spotsDaily ChallengesNew coding challenges every dayRace ModeCompete against others in real timeAI OpponentRace against an AI at your WPM levelTournamentsLive coding speed tournamentsArcade GamesZType, Overkill Survival, Glyphica & moreGamificationXP, coins, badges & quests
LeaderboardGlobal rankings for every languageCertificatesEarn verifiable Bronze / Silver / Gold certsActivityDaily streaks & historical analyticsProfileYour stats, badges & achievements
Browse Languages500+ languages with real code examplesBlogTips, guides & deep divesFree ToolsWPM calculator, typing speed report & moreFAQCommon questions answeredGetting StartedNew to CodeSpeedTest?AboutOur story & missionSupportGet help — Pro users get priorityContactGet in touch with the team
Pricing
  1. Home
  2. /
  3. Learn
  4. /
  5. Ink

Learn Ink - 10 Code Examples & CST Typing Practice Test

Ink! is a Rust-based eDSL (embedded domain-specific language) for writing smart contracts on the Substrate blockchain framework. It emphasizes safety, efficiency, and tight integration with Polkadot and Substrate ecosystems.

View all 10 Ink code examples →
Ink! Simple Counter ContractInk! Owner-Only MessageInk! Simple ERC20 TokenInk! Simple Vault (Deposit + Withdraw)Ink! Timelock ContractInk! Simple VotingInk! Whitelist AccessInk! Event LoggerInk! Immutable Config ExampleInk! Simple Multiplier

Learn INK with Real Code Examples

Updated Nov 25, 2025

Explain

Ink! uses Rust syntax with domain-specific macros for blockchain smart contracts.

It provides compile-time safety and strong typing, reducing runtime errors.

Contracts compile to WebAssembly (Wasm) to run on Substrate-based chains.

Designed for formal verification and predictable execution.

Used in DeFi, NFTs, and governance applications within Polkadot and Kusama ecosystems.

Core Features

#[ink(storage)] for state

#[ink(event)] for events

#[ink(message)] for callable functions

Cross-contract calls

Metadata generation for contracts

Basic Concepts Overview

Storage struct holds contract state

Messages are public callable functions

Events record contract activity

Cross-contract calls allowed via ink! traits

Constructor initializes contract state

Project Structure

lib.rs - main contract code

Cargo.toml - Rust project config

tests/ - Rust unit tests

target/ - compiled Wasm artifacts

metadata.json - ABI for frontend interaction

Building Workflow

Write Rust + Ink! macros

Compile to Wasm

Generate metadata

Deploy to Substrate testnet

Interact via Polkadot.js or API

Difficulty Use Cases

Beginner: simple storage contract

Intermediate: token contracts

Advanced: NFT marketplace

Expert: multi-contract governance logic

Auditor: Wasm & Rust contract analysis

Comparisons

Ink! vs Solidity: Ink! is Rust-based and Wasm-native; Solidity is EVM-native.

Ink! vs Vyper: Ink! runs on Substrate/Wasm; Vyper runs on EVM.

Ink! vs Solidity + EVM: Ink! has strong Rust type safety; Solidity has larger ecosystem.

Ink! vs Move: Move is resource-based, Ink! is Rust/Wasm based.

Ink! vs Cairo: Cairo targets STARKs; Ink! targets Substrate/Wasm chains.

Versioning Timeline

2018 - Initial Ink! prototypes

2019 - ink! 1.0

2020 - ink! 2.0 with macros

2021 - PSP standards introduced

2022-2025 - Continuous tooling and compiler improvements

Glossary

Wasm: WebAssembly

Storage: contract state

Message: callable function

Event: emitted log

Constructor: initializer function

Installation Setup

Install Rust toolchain

Install cargo-contract CLI

Set up Substrate node or local testnet

Compile contract with `cargo contract build`

Deploy using `cargo contract deploy` or Polkadot.js

Environment Setup

Install Rust toolchain

Install cargo-contract CLI

Set up Substrate node

Build & compile ink! contract

Deploy to local/testnet

Config Files

Cargo.toml

lib.rs

target/ for Wasm artifacts

metadata.json

tests/ folder

Cli Commands

cargo contract build

cargo contract deploy

cargo contract test

cargo contract generate-metadata

cargo contract call

Internationalization

Docs in multiple languages

Community tutorials globally

Polkadot ecosystem support

Unicode-safe contracts

Global developer reach

Accessibility

Rust familiarity helps

Strong compiler messages

Macros reduce boilerplate

Predictable execution

Comprehensive documentation

Ui Styling

Frontend via Polkadot.js

Not applicable in backend Rust code

IDE syntax highlighting for Rust

cargo-contract CLI output

Metadata-driven frontend forms

State Management

Storage struct fields

Immutable constants

Mappings via StorageHashMap

Event logs

Cross-contract state access

Data Management

Wasm memory model

Typed storage fields

Bounded collections for safety

Cross-contract message payloads

Event-indexed logs

Architecture

Rust compiler -> Wasm bytecode

Storage structs define state

Messages as external callable functions

Event logging integrated in Wasm

Metadata defines contract ABI for frontends

Rendering Model

Rust code -> ink! macros -> Wasm bytecode

Contract metadata for ABI

Storage struct layout

Event emission model

Cross-contract call ABI

Architectural Patterns

Storage struct for state

Messages as public API

Events for logging

Traits for modular calls

Composable multi-contract logic

Real World Architectures

DeFi lending & staking

NFT marketplaces

On-chain governance modules

Cross-contract DeFi protocols

Parachain-specific applications

Design Principles

Safety and type guarantees

Wasm-native execution

Rust ecosystem leverage

Predictable gas and storage

Composable contracts

Scalability Guide

Use traits for modularity

Keep Wasm size manageable

Optimize storage layout

Split contracts logically

Use metadata efficiently

Migration Guide

Rewrite Solidity/Vyper contracts in Rust + Ink!

Replace storage variables with #[ink(storage)]

Rewrite public functions as #[ink(message)]

Map events using #[ink(event)]

Deploy on Substrate instead of EVM

Performance Notes

Wasm contracts run efficiently on Substrate

Predictable gas and storage usage

Compile-time safety reduces runtime failures

Loops and recursion must be bounded

Large contracts may increase deployment size

Security Notes

Strong type safety reduces errors

No undefined behavior thanks to Rust

Events prevent hidden state changes

Cross-contract call safety via traits

Formal verification easier due to Rust foundations

Monitoring Analytics

Event log tracking

Gas usage analysis

Contract interaction analytics

Audit tool outputs

Frontend integration metrics

Code Quality

Follow Rust best practices

Use macros for boilerplate

Document messages and events

Keep storage minimal

Unit-test all functions

Practical Examples

Simple key-value store

ERC-20-like token

NFT minting and transfer

Voting and governance module

Cross-contract calls for DeFi pools

Troubleshooting

Check Rust compiler errors

Ensure ink! macros used correctly

Verify storage types

Validate event definitions

Test cross-contract calls

Testing Guide

Write Rust unit tests in tests/ folder

Use cargo-contract test

Validate storage updates

Check event emissions

Test cross-contract interactions

Deployment Options

Local Substrate node

Polkadot testnet

Kusama parachain

Polkadot.js deploy

CI/CD pipelines with cargo-contract

Tools Ecosystem

cargo-contract CLI

Substrate node template

Polkadot.js front-end

Ink! unit testing in Rust

ink! playground (online)

Integrations

Polkadot and Kusama chains

Substrate-based parachains

Frontends via Polkadot.js

Off-chain workers

Cross-contract modules within Substrate

Productivity Tips

Leverage ink! macros

Write modular contracts

Unit-test extensively

Keep Wasm code minimal

Use cargo-contract CLI efficiently

Challenges

Rust complexity for beginners

Wasm contract size limits

Cross-contract interaction nuances

Limited pre-built libraries

Debugging Wasm can be tricky

Learning Path

Learn Rust basics

Understand Substrate architecture

Write simple ink! contracts

Compile and deploy Wasm

Test, audit, and deploy production contracts

Skill Improvement Plan

Week 1: Rust syntax and ownership

Week 2: ink! storage & messages

Week 3: Events and cross-contract calls

Week 4: Unit testing & simulation

Week 5: Deploy real-world applications on Substrate

Interview Questions

Why choose ink! over Solidity?

Explain ink! storage macros

How are events emitted in ink!?

Describe cross-contract calls in ink!

How does ink! integrate with Substrate?

Cheat Sheet

#[ink(storage)] -> contract state struct

#[ink(event)] -> blockchain event

#[ink(message)] -> public function

Constructor via #[ink(constructor)]

Traits for cross-contract calls

Books

Mastering Substrate Development

Ink! Smart Contract Development

Building DeFi on Polkadot

Practical Wasm Smart Contracts

Rust for Blockchain Developers

Tutorials

Write your first ink! contract

PSP22 token example

NFT contract using PSP34

Cross-contract interaction tutorial

Testing and deploying ink! contracts

Official Docs

https://paritytech.github.io/ink-docs/

https://github.com/paritytech/ink

Community Links

Ink! GitHub Discussions

Substrate Builders Program

Polkadot Developer Forums

Rust + Ink! Discord channels

YouTube tutorials and courses

Community Support

Ink! GitHub

Substrate Builders Program

Polkadot Developer Forums

Rust + Ink! Discord channels

YouTube tutorials and courses

Monetization

Develop Polkadot DeFi contracts

Offer audited NFT templates

Consult on ink! contract development

Sell governance modules

Educational workshops

Future Roadmap

Better Rust tooling

Expanded PSP standards

IDE & debugging support

Improved cross-contract calls

More Substrate ecosystem integration

When Not To Use

EVM-based chains

Projects requiring Solidity tooling

Developers unfamiliar with Rust

Contracts targeting non-Substrate chains

Projects needing large pre-existing libraries from Solidity

Final Summary

Ink! is a Rust-based smart contract language for Substrate.

Safe, high-performance, Wasm-targeted.

Ideal for DeFi, NFTs, governance, and blockchain-native apps.

Leverages Rust's type safety and macros for secure contracts.

Perfect for developers targeting Polkadot/Kusama ecosystems.

Faq

Is ink! Rust-based?

Yes - fully built on Rust.

Can ink! run on EVM?

No - targets Wasm/Substrate.

Is ink! safe for DeFi?

Yes - Rust safety + compile-time checks.

Does ink! support NFTs?

Yes - with PSP34 standard.

Can ink! contracts call each other?

Yes - via cross-contract trait calls.

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.

Let’s Try →
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.

Let’s Try →
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.

Let’s Try →
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.

Let’s Try →
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.

Let’s Try →
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!.

Let’s Try →
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.

Let’s Try →
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.

Let’s Try →
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!.

Let’s Try →
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.

Let’s Try →

Frequently Asked Questions about Ink

What is Ink?

Ink! is a Rust-based eDSL (embedded domain-specific language) for writing smart contracts on the Substrate blockchain framework. It emphasizes safety, efficiency, and tight integration with Polkadot and Substrate ecosystems.

What are the primary use cases for Ink?

High-security smart contracts. DeFi protocols on Substrate. NFT minting and marketplaces. On-chain governance modules. Wasm-based blockchain apps

What are the strengths of Ink?

Safe, type-checked contracts. High performance via Wasm. Strong ecosystem support in Polkadot. Auditable Rust code. Predictable gas and storage behavior

What are the limitations of Ink?

Limited to Substrate/Wasm chains. Smaller ecosystem than Solidity/EVM. Requires Rust proficiency. No EVM compatibility. Less tooling for testing vs EVM chains

How can I practice Ink typing speed?

CodeSpeedTest offers 10+ real Ink code examples for typing practice. You can measure your WPM, track accuracy, and improve your coding speed with guided exercises.

Learn Other Programming Languages

CReactPythonC++RustTypeScriptKotlinPHPJavaC#RubyMqlCqlN1qlCypherGremlinPartiqlHaskellElixirFsharpView all languages →
CodeSpeedTest

Improve your coding speed, code accuracy, and programming syntax WPM with practice sessions across 500+ programming languages.

Quick Links

HomeAboutFeaturesGetting StartedLanguages

Legal & Support

Pro ⚡ PricingContactPrivacy PolicyTerms of Service

Connect

CodeSpeedTest on GitHubCodeSpeedTest on TwitterEmail CodeSpeedTest

© 2026 CodeSpeedTest. All rights reserved.