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:
- Wasm-based smart contracts - using ink!, a Rust-based embedded domain-specific language (eDSL), enabling developers to leverage Rust’s safety and tooling
- EVM-compatible contracts - which support languages like Solidity and Vyper, offering compatibility with popular Ethereum tools and wallets
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.
Additional information
To read more about the differences between smart contracts and parachain runtimes, please Refer to the Runtime vs. Smart Contracts section of the Polkadot SDK Rust docs. For a more in-depth discussion on choosing between runtime development and smart contract development, you can check the post "When should one build a Polkadot SDK runtime versus a Substrate (Polkadot SDK) smart contract?" from Stack Overflow.
Building a Smart Contract¶
Polkadot's primary purpose is to provide security for parachains that connect to it. Therefore, it is not meant to support smart contract execution. Developers looking to build smart contract projects in Polkadot need to look into its ecosystem for parachains that support it.
The Polkadot SDK supports multiple smart contract execution environments:
- 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 - through the Contracts pallet. ink! is a smart contract language that provides a compiler to Wasm. Wasm contracts can be used by chains like Astar
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
Broadly speaking, in this configuration, an EVM transaction follows the path presented in the diagram below:
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! - it is 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 - it 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
Broadly speaking, with pallet_contracts
, a transaction follows the path presented in the diagram below:
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
Learn more on how to build and deploy Wasm smart contracts on the Wasm Smart Contracts page.
| Created: October 16, 2024