MFS402 : Table of Contents

Chapter 2

Lesson 1

Foundational

What is Ink! and why it's used in Polkadot

Overview of Rust as the base language for Ink!

Advanced

The structure of an Ink! smart contract

Deploying smart contracts in parachains

Quiz it to win it

Take the quiz

1 Questions
12 XP

What is Ink! and Why It’s Used in Polkadot?


Ink! is a powerful smart contract language designed specifically for the Polkadot ecosystem. Ink! is the fastest way to get started in Polkadot ecosystem. It is one of the cleanest smart contract language and a joy to write in.

While Polkadot is famous as a layer-0 infrastructure layer for building layer-1 Blockchains (aka parachains), it is absolutely not needed to start there. In fact, unless there is a specific need, we recommend to understand building smart contracts for Substrate-compatible Blockchains — and ink! is the best candidate for that.

Mature toolkits exist to build, deploy, and manage Ink!-based smart contracts on Substrate-based blockchains. Instead of a brand new language, Ink! uses Rust as a foundational layer. This makes writing contracts a seamless and 'native' experience.

Ultimately, these contracts can be compiled into webAssembly (Wasm), making them performant, interoperable across different Blockchains in Polkadot, and more. These contracts can be deployed on most parachains that support smart contracts, including popular testnets and mainnets like Moonbeam, Astar, and Rococo. They can also aid to custom Blockchain development — but more on that later.

Why Ink! is Native to Polkadot


  • Rust Support: Ink! is written in Rust, a language known for its performance and safety. Rust’s memory management and concurrency features make it ideal for blockchain development.
  • Compiles to WebAssembly: Wasm is a portable and efficient bytecode format used by Polkadot for its runtime and smart contracts. Ink! compiles directly to Wasm, ensuring compatibility with Polkadot’s ecosystem.
  • Integration with Substrate Pallets: Ink! contracts can seamlessly interact with native Substrate-based nodes and their runtime modules (pallets). This opens up possibilities for advanced integrations—we’ll explore this later.
  • Ink! and Rust: How Are They Related?


    Ink! is an Embedded Domain Specific Language (eDSL) built on top of Rust. Simply put, it extends Rust with specialized macros and annotations to enable blockchain-specific functionalities.

    Macro-based Design


    Ink! uses macros like #[ink::contract], #[ink::storage], and #[ink::message] to define the core components of a smart contract. These macros tell the Ink! compiler how to process our code for blockchain use.

    Domain-Specific Enhancements


    Ink! provides tools for interacting with the blockchain environment, such as accessing the caller's account ID or transferring tokens.

    Think of Ink! as Rust with superpowers for blockchain. It's still Rust at its core but tailored for Web3 development.

    The Anatomy of an ink! Smart Contract


    Before we dive deeper into ink!, let's highlight the key pieces of a minimal ink! smart contract.

    For this example, we take the Flipper contract. It has a single boolean variable (value) that can be toggled between true and false. It may not have all the bells and whistles, but we aim to detail the most important bits of an ink! contract.

    But first, let's get the CLI that will help us with setting up and managing smart contracts written with ink!.

    This enable us to quickly start up ink! templates. In a suitable folder, let's create a template ink! contract.

    Consequently, this will create a flipper folder with two key files:

  • Cargo.toml: This is the standard config file for Rust projects.
  • lib.rs: The crux of the contract.
  • For simplicity, we will replace the content in lib.rs with an even simpler version of the flipper contract.

    Breaking It Down


    If the std feature is not enabled in the `Cargo.toml` file, the contract switches to no_std, which excludes Rust's standard library from the compilation process. This is a crucial step for WebAssembly (Wasm) compatibility, as Wasm's lightweight design prioritizes performance and portability.

    This declares the module as an Ink! smart contract. Every contract needs exactly one of these.

    Storage: #[ink(storage)] marks the Flipper struct as the storage for this contract. Each contract must have one storage struct to persist data on-chain. While we will explore all storage options in depth in a later chapter, it's important to note that the storage struct is where the contract keeps its persistent state. Whether it's a simple value like this boolean or more complex data structures, all on-chain data resides here — ensuring it remains available between contract interactions.

    This is the primary constructor that allows you to explicitly set the initial value of the flipper. It's more flexible as it lets you create a Flipper instance with either true or false as the starting value.

    default() - This is a convenience constructor that creates a Flipper instance with a default value (false). It delegates to the new constructor by using Default::default() which returns false for bool types in Rust.

    Having multiple constructors is a common pattern in smart contracts for a few reasons:

  • It provides different ways to initialize the contract based on different use cases.
  • The default() constructor follows the Rust convention of providing a default initialization.
  • It makes the contract more user-friendly by offering both explicit initialization (new) and a simple default option (default)
  • This pattern is particularly useful in smart contract contexts where we want to give users flexibility in how they deploy and initialize contracts while still maintaining safe defaults.

    Messages: #[ink(message)] functions define how users interact with the contract.

    flip toggles the value by switching the boolean stored in the contract's state. This is a mutable operation that changes the contract's persistent storage, making it the core action of this contract.

    get retrieves the current value of the boolean. This is a read-only operation, meaning it doesn't alter the state but instead provides external users with the current status of the contract. This separation of state-changing and query functions is a fairly common and important design principle in smart contract development.

    Deploying Ink! Contracts


    First, we compile the contract with cargo-contract.

    This will generate the .contract and .wasm files in the target directory.

  • The .contract file includes both the contract's bytecode and metadata.
  • The bytecode is .wasm, which is stored onchain when the contract is deployed.
  • Now, this is where contract deployment takes on a whole new meaning in Polkadot compared to other blockchains. Unlike typical systems, deploying a contract in Polkadot involves not just placing bytecode on-chain but also instantiating the contract. Deployment creates the blueprint (bytecode and metadata stored on the blockchain), while instantiation spins up a live instance of the contract that users can interact with. This two-step process is especially useful for scenarios where multiple instances of a single contract are needed, as you only need to store the bytecode once.
  • The .contract file can be used to upload the contract to local nodes or parachains supporting smart contracts. More on that in coming chapters!
  • This lesson provided an overview of Ink!, its relationship with Rust, and how to use the Flipper contract to understand its structure. In the next chapters, we'll dive deeper into advanced features of Ink! and explore real-world use cases.
  • Quiz it to win it

    Complete this quiz successfully to proceed to the next lesson and win upto 12XP.

    Start quiz for this lesson

    Completing this quiz will get you

    +12 Experience Points

    +4% course progress