Substrate Framework: Building Blockchains with Polkadot

Learn to build custom blockchains using Substrate, the framework powering Polkadot.

Advanced⏱️ 60 min📚 Prerequisites: 2

Substrate Framework: Building Blockchains with Polkadot

Substrate is a blockchain framework written in Rust that enables building custom blockchains.

What is Substrate?

Substrate provides:

  • Modular Architecture: Mix and match components
  • Runtime Logic: Write in Rust, compile to WebAssembly
  • Consensus: Built-in or custom consensus mechanisms
  • Networking: P2P networking out of the box
  • Forkless Upgrades: Upgrade without hard forks

Key Concepts

Runtime

The runtime contains the business logic of your blockchain. It's compiled to WebAssembly for execution.

RUST
// Substrate runtime structure
#[frame_support::pallet]
pub mod pallet_example {
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;
    
    #[pallet::config]
    pub trait Config: frame_system::Config {
        type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
    }
    
    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);
    
    #[pallet::storage]
    pub type Value<T: Config> = StorageValue<_, u32>;
    
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {
        ValueStored(u32, T::AccountId),
    }
    
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #[pallet::weight(10_000)]
        pub fn set_value(origin: OriginFor<T>, value: u32) -> DispatchResult {
            let who = ensure_signed(origin)?;
            Value::<T>::put(value);
            Self::deposit_event(Event::ValueStored(value, who));
            Ok(())
        }
    }
}

Pallets

Pallets are modules that provide specific functionality:

  • pallet-balances: Token balances
  • pallet-timestamp: Time tracking
  • pallet-staking: Staking functionality
  • pallet-democracy: Governance

Extrinsics

Extrinsics are transactions or calls that modify state.

Events

Events are emitted when state changes occur.

Building a Custom Chain

1. Install Substrate

BASH
curl https://getsubstrate.io -sSf | bash -s -- --fast

2. Create a Node

BASH
substrate-node-new my-blockchain alice

3. Create a Runtime Module

BASH
substrate-module-new my-pallet

Key Features

Forkless Upgrades

Substrate enables runtime upgrades without hard forks:

RUST
// Upgrade runtime via governance
pub fn set_code(new_code: Vec<u8>) -> DispatchResult {
    // Code upgrade logic
}

Custom Consensus

Choose from:

  • Aura: Block authoring
  • BABE: Block production
  • GRANDPA: Finality gadget
  • Custom consensus

Interoperability

Substrate chains can connect to Polkadot/Parachains:

  • Parachains: Connected to relay chain
  • Bridges: Connect to other chains

Real-World Examples

  • Polkadot: Relay chain built with Substrate
  • Kusama: Canary network
  • Moonbeam: Ethereum-compatible parachain
  • Acala: DeFi parachain

Advantages

  • Fast Development: Pre-built components
  • Security: Battle-tested code
  • Flexibility: Customize as needed
  • Interoperability: Connect to Polkadot ecosystem
  • Upgradeability: No hard forks needed

Code Examples

Simple Substrate Pallet

Basic pallet structure

RUST
// Simplified Substrate pallet concept
use std::collections::HashMap;

struct Pallet {
    storage: HashMap<String, u64>,
}

impl Pallet {
    fn new() -> Self {
        Pallet {
            storage: HashMap::new(),
        }
    }
    
    fn set_value(&mut self, key: String, value: u64) -> Result<(), String> {
        self.storage.insert(key, value);
        Ok(())
    }
    
    fn get_value(&self, key: &str) -> Option<u64> {
        self.storage.get(key).copied()
    }
    
    fn emit_event(&self, event: &str) {
        println!("Event: {}", event);
    }
}

fn main() {
    let mut pallet = Pallet::new();
    
    // Set value (extrinsic)
    pallet.set_value(String::from("balance"), 1000).unwrap();
    
    // Emit event
    pallet.emit_event("ValueStored");
    
    // Get value
    if let Some(value) = pallet.get_value("balance") {
        println!("Balance: {}", value);
    }
}

Explanation:

Substrate pallets are modules that provide blockchain functionality. They have storage (state), extrinsics (transactions), and events (notifications). This simplified example shows the basic structure.

Runtime Upgrade Concept

Conceptual runtime upgrade mechanism

RUST
// Simplified runtime upgrade concept
struct Runtime {
    version: u32,
    code: Vec<u8>,
}

impl Runtime {
    fn new() -> Self {
        Runtime {
            version: 1,
            code: vec![1, 2, 3, 4],
        }
    }
    
    fn upgrade(&mut self, new_code: Vec<u8>) -> Result<(), String> {
        // In Substrate: validate new code, set new runtime
        self.code = new_code;
        self.version += 1;
        println!("Runtime upgraded to version {}", self.version);
        Ok(())
    }
    
    fn execute(&self, call: &str) -> Result<String, String> {
        // Execute runtime logic
        Ok(format!("Executed: {}", call))
    }
}

fn main() {
    let mut runtime = Runtime::new();
    
    println!("Current version: {}", runtime.version);
    
    // Upgrade runtime (forkless!)
    runtime.upgrade(vec![5, 6, 7, 8]).unwrap();
    
    // Execute with new runtime
    println!("{}", runtime.execute("set_value").unwrap());
}

Explanation:

Substrate enables forkless upgrades by storing runtime code on-chain. The runtime can be upgraded via governance without requiring a hard fork. This is a key advantage of Substrate.

Exercises

Simple Storage Pallet

Create a simple storage pallet!

Medium

Starter Code:

RUST
use std::collections::HashMap;

struct Pallet {
    storage: HashMap<String, u64>,
}

fn main() {
    // Create pallet
    // Store a value
    // Retrieve the value
}