An Overview of the Smart Contract Landscape on Polkadot¶
Introduction¶
Polkadot is designed to support an ecosystem of parachains, rather than hosting smart contracts directly. Developers aiming to build smart contract applications on Polkadot rely on parachains within the ecosystem that provide smart contract functionality.
This guide outlines the primary approaches to developing smart contracts in the Polkadot ecosystem:
- PolkaVM-compatible contracts - which support Solidity and any language that compiles down to RISC-V while maintaining compatibility with Ethereum based tools
- EVM-compatible contracts - which support languages like Solidity and Vyper, offering compatibility with popular Ethereum tools and wallets
- Wasm-based smart contracts - using ink!, a Rust-based embedded domain-specific language (eDSL), enabling developers to leverage Rust’s safety and tooling
You'll explore the key differences between these development paths, along with considerations for parachain developers integrating smart contract functionality.
Parachain Developer?
If you are a parachain developer looking to add smart contract functionality to your chain, please refer to the Add Smart Contract Functionality page, which covers both Wasm and EVM-based contract implementations.
Smart Contracts Versus Parachains¶
A smart contract is a program that executes specific logic isolated to the chain on which it is being executed. All the logic executed is bound to the same state transition rules determined by the underlying virtual machine (VM). Consequently, smart contracts are more streamlined to develop, and programs can easily interact with each other through similar interfaces.
flowchart LR
subgraph A[Chain State]
direction LR
B["Program Logic and Storage<br/>(Smart Contract)"]
C["Tx Relevant Storage"]
end
A --> D[[Virtual Machine]]
E[Transaction] --> D
D --> F[(New State)]
D --> G[Execution Logs]
style A fill:#ffffff,stroke:#000000,stroke-width:1px
In addition, because smart contracts are programs that execute on top of existing chains, teams don't have to think about the underlying consensus they are built on.
These strengths do come with certain limitations. Some smart contracts environments, like EVM, tend to be immutable by default. Developers have developed different proxy strategies to be able to upgrade smart contracts over time. The typical pattern relies on a proxy contract which holds the program storage forwarding a call to an implementation contract where the execution logic resides. Smart contract upgrades require changing the implementation contract while retaining the same storage structure, necessitating careful planning.
Another downside is that smart contracts often follow a gas metering model, where program execution is associated with a given unit and a marketplace is set up to pay for such an execution unit. This fee system is often very rigid, and some complex flows, like account abstraction, have been developed to circumvent this problem.
In contrast, parachains can create their own custom logics (known as pallets or modules), and combine them as the state transition function (STF or runtime) thanks to the modularity provided by the Polkadot-SDK. The different pallets within the parachain runtime can give developers a lot of flexibility when building applications on top of it.
flowchart LR
A[(Chain State)] --> B[["STF<br/>[Pallet 1]<br/>[Pallet 2]<br/>...<br/>[Pallet N]"]]
C[Transaction<br/>Targeting Pallet 2] --> B
B --> E[(New State)]
B --> F[Execution Logs]
Parachains inherently offer features such as logic upgradeability, flexible transaction fee mechanisms, and chain abstraction logic. More so, by using Polkadot, parachains can benefit from robust consensus guarantees with little engineering overhead.
To read more about the differences between smart contracts and parachain runtimes, see the Runtime vs. Smart Contracts section of the Polkadot SDK Rust docs.
For a more in-depth discussion about choosing between runtime development and smart contract development, see the post "When should one build a Polkadot SDK runtime versus a Substrate (Polkadot SDK) smart contract?" from Stack Overflow.
Building a Smart Contract¶
The Polkadot SDK supports multiple smart contract execution environments:
- PolkaVM - a cutting-edge virtual machine tailored to optimize smart contract execution on Polkadot. Unlike traditional EVMs, PolkaVM is built with a RISC-V-based register architecture for increased performance and scalability
- EVM - through Frontier. It consists of a full Ethereum JSON RPC compatible client, an Ethereum emulation layer, and a Rust-based EVM. This is used by chains like Acala, Astar, Moonbeam and more
- Wasm - ink! is a domain-specific language (DSL) for Rust smart contract development that uses the Contracts pallet with
cargo-contract
serving as the compiler to WebAssembly. Wasm contracts can be used by chains like Astar
PolkaVM Contracts¶
A component of the Asset Hub parachain, PolkaVM helps enable the deployment of Solidity-based smart contracts directly on Asset Hub. Learn more about how this cutting edge virtual machine facilitates using familiar EVM contracts and tools with Asset Hub by visiting the Native EVM Contracts guide.
EVM Contracts¶
The Frontier project provides a set of modules that enables a Polkadot SDK-based chain to run an Ethereum emulation layer that allows the execution of EVM smart contracts natively with the same API/RPC interface.
Ethereum addresses (ECDSA) can also be mapped directly to and from the Polkadot SDK's SS58 scheme from existing accounts. Moreover, you can modify Polkadot SDK to use the ECDSA signature scheme directly to avoid any mapping.
At a high level, Frontier is composed of three main components:
- Ethereum Client - an Ethereum JSON RPC compliant client that allows any request coming from an Ethereum tool, such as Remix, Hardhat or Foundry, to be admitted by the network
- Pallet Ethereum - a block emulation and Ethereum transaction validation layer that works jointly with the Ethereum client to ensure compatibility with Ethereum tools
- Pallet EVM - access layer to the Rust-based EVM, enabling the execution of EVM smart contract logic natively
The following diagram illustrates a high-level overview of the path an EVM transaction follows when using this configuration:
flowchart TD
A[Users and Devs] -->|Send Tx| B[Frontier RPC Ext]
subgraph C[Pallet Ethereum]
D[Validate Tx]
E[Send<br/>Valid Tx]
end
B -->|Interact with| C
D --> E
subgraph F[Pallet EVM]
G[Rust EVM]
end
I[(Current EVM<br/>Emulated State)]
H[Smart Contract<br/>Solidity, Vyper...] <-->|Compiled to EVM<br/>Bytecode| I
C --> F
I --> F
F --> J[(New Ethereum<br/>Emulated State)]
F --> K[Execution Logs]
style C fill:#ffffff,stroke:#000000,stroke-width:1px
style F fill:#ffffff,stroke:#000000,stroke-width:1px
Although it seems complex, users and developers are abstracted of that complexity, and tools can easily interact with the parachain as they would with any other EVM-compatible environment.
The Rust EVM is capable of executing regular EVM bytecode. Consequently, any language that compiles to EVM bytecode can be used to create programs that the parachain can execute.
Wasm Contracts¶
The pallet_contracts
provides the execution environment for Wasm-based smart contracts. Consequently, any smart contract language that compiles to Wasm can be executed in a parachain that enables this module.
At the time of writing there are two main languages that can be used for Wasm programs:
- ink! - a Rust-based language that compiles to Wasm. It allows developers to inherit all its safety guarantees and use normal Rust tooling, being the dedicated domain-specific language
- Solidity - can be compiled to Wasm via the Solang compiler. Consequently, developers can write Solidity 0.8 smart contracts that can be executed as Wasm programs in parachains
The following diagram illustrates a high-level overview of the path a transaction follows when using pallet_contracts
:
flowchart TD
subgraph A[Wasm Bytecode API]
C[Pallet Contracts]
end
B[Users and Devs] -- Interact with ---> A
D[(Current State)]
E[Smart Contract<br/>ink!, Solidity...] <-->|Compiled to Wasm<br/>Bytecode| D
D --> A
A --> F[(New State)]
A --> G[Execution Logs]
style A fill:#ffffff,stroke:#000000,stroke-width:1px
| Created: March 6, 2025