Solidity Smart Contracts on Polkadot

Pendulum
Pendulum-Chain
Published in
9 min readOct 18, 2023

Smart contracts are self-executing contracts with the terms of agreement between buyer and seller being directly written into lines of code. They are decentralized applications that run on a blockchain network.

The Polkadot relay chain does not natively support smart contracts, but since the parachains that connect to Polkadot can support arbitrary state transitions, they do support smart contracts. Substrate presently supports smart contracts in several ways, including the EVM pallet offered by Frontier and the Contracts pallet in the FRAME library for Wasm-based contracts.

In this article we will discuss how Solidity contracts can be used without using EVM interpreters, the benefits of using Solang to compile Solidity smart contracts, how Solidity smart contracts can interact with Substrate runtimes, and finally argue about the importance of these learnings for the Polkadot ecosystem.

Solang contract compilation

Solang is a new Solidity compiler that supports compiling smart contracts written in Solidity for Solana and Polkadot. It is designed to provide improved performance, compatibility with multiple blockchains, and ease of use.

Compatibility with Multiple Blockchains

Solang is designed to be compatible with multiple blockchains. This means that smart contracts compiled with Solang can be used on different blockchain networks, including Ethereum, Polkadot, and others. This is a significant advantage for developers because it allows them to write smart contracts once and deploy them on different networks without having to write different codes for each network.

The following graphic shows how programs implemented in Solidity, ink!, and the Wasm text format (Wat) can be compiled and deployed on a selection of executors on Ethereum and Substrate-based chains.

Visualization of the compilation and deployment flow

Improved Performance

Solang is a Rust-based compiler that has been designed to provide high performance. Rust is a memory-safe programming language that is known for its speed and reliability. Solang is capable of producing WebAssembly (Wasm) code that is optimized for execution on a blockchain network. This means that smart contracts compiled with Solang are likely to be faster and more efficient than those compiled with other compilers. As shown in our previous blog post “The Bright Future of WebAssembly smart contracts”, compiling Solidity code via Solang to Wasm and executing it on the Contracts pallet can be more performant than executing the Solidity bytecode on an EVM engine like Go Ethereum.

The execution time of two benchmark projects (’SHA-512’ and ‘Odd Numbers’) was implemented for and deployed on different executors.

ink!, The contracts pallet, & Chain Extensions

The ink! framework is a Rust-based framework for building smart contracts on Substrate that supports the Rust programming language and provides a high-level language for writing smart contracts. The ink! framework includes a set of macros and tools for building smart contracts, compiling them to WebAssembly bytecode, deploying them to a Substrate-based blockchain network, and interacting with them.

The Contracts pallet, on the other hand, is a built-in pallet in the Substrate FRAME library that provides support for Wasm-based smart contracts. It includes functionalities such as creating and executing contracts, querying contract storage, and managing contract events. However, it only exposes a limited set of functions for smart contracts to interact with the runtime. To expose more functionality, developers can use chain extensions to connect smart contracts to custom functions defined by runtime developers.

As mentioned, the smart contracts that are deployed to and executed by the contracts pallet don’t have access to the data of other pallets by default. This is why we need to extend the limited interface available to smart contracts by default with custom logic that controls interaction with the asset-managing pallet.

Smart Contract Asset Handling on Pendulum

To attract builders of existing DeFi projects, we want to make it as convenient as possible to start building on Pendulum. That’s why we are aiming for a solution that does not require any changes to the existing implementation of DeFi projects written in Solidity. This section describes how this can be achieved while keeping the asset management in sync between contracts and runtime.

Tokens on Ethereum and alike are usually represented as instances of the ERC-20 interface. Since DeFi requires a lot of token interaction, the implementations commonly expect interaction with instances of the ERC-20 contract to read and change the state of assets on-chain.

On most Substrate-based chains, the asset handling is done by pallets like pallet-assets or orml-tokens. To enable interaction between smart contracts and on-chain asset data (stored in Substrate pallets), we created the Pendulum smart contract wrappers. By using the Solang compiler, we can compile Solidity smart contracts to Wasm binaries that can be indeployed to the Contracts pallet. The on-chain asset data is then exposed with the asset chain extension, which facilitates interaction between the smart contract and pallets.

The ink! framework provides logic for the interaction between smart contracts and chain extensions. Fortunately, the Solang compiler has integrated support for these chain extensions so when compiling Solidity contracts for Substrate chains using Solang, developers have access to the chain_extension function, making them able to call into the runtime without having to use the ink! in the smart contract itself.

Solidity smart contracts on Polkadot can interact with each other using the call() function, which requires the address of the contract being called and the amount of value being transferred. The called contract then executes its code and returns any generated data.

The following graphic gives an overview of the components and interactions involved in the asset handling by smart contracts on Pendulum. The logic is explained in more detail in the following sections.

A high-level overview of the compilation flow of the wrapper contracts and their interactions

The wrapper contracts and chain extension were recently audited by the Hacken auditing agency. You can find more details about this audit in our previous blog post here.

Asset Chain Extension

Pendulum’s asset chain extension implements the following interface:

// IERC20 
fn totalSupply(currency: CurrencyId) -> Balance
fn balanceOf(currency: CurrencyId, account: AccountId) -> Balance
fn transfer(currency: CurrencyId, recipient: AccountId, amount: Balance)
fn allowance(currency: CurrencyId, owner: AccountId, spender: AccountId) -> Balance
fn approve(currency: CurrencyId, spender: AccountId, amount: Balance)
fn transfer_from(sender: AccountId, currency: CurrencyId, recipient: AccountId, amount: Balance)
// price oracle
fn get_coin_info(blockchain: String, symbol: String)

These functions are callable from smart contracts and manage reading and changing the state of the asset and price oracle pallets on Pendulum.

Creating secure, non-exploitable chain extensions is a delicate process. It is important that these functions do not expose exploitable code and charge an appropriate amount of weight before carrying out any action that consumes resources. To access the auditing agency’s security assessment for Pendulum’s chain extension, click here.

Adding Delegated Transfers to the Tokens Pallet

On Pendulum, we use ORML’s Tokens pallet. While this pallet provides commonly used functionalities for fungible multi-currencies, it lacks support for allowances and delegated asset transfers (i.e. approve() and transfer_from() functionality).

Since delegated transfers are heavily used in DeFi projects, we needed to find a way to support this on a runtime level. The orml-currencies-allowance-extension pallet was created to extend the Tokens pallet with support for delegated transfers. For a set of allow-listed currencies, users can use this pallet to approve amounts for another account to spend on their behalf and eventually execute the pre-approved transfer from another account. The chain extension facilitates the extrinsics provided by this pallet to fully support the IERC20 interface.

Solidity Wrapper Contracts

As mentioned, we want to be able to deploy existing Solidity DeFi projects as-is on Pendulum. This requires us to provide smart contracts that fulfill the expected interfaces.

To fulfill these interfaces, Pendulum offers two Solidity wrapper contracts: 1) a wrapper contract for the IERC-20 interface and 2) a wrapper contract for price oracles.

The idea is that the relevant data is not stored within the wrapper contracts themselves. Instead, they only implement the interface of the wrapped contract and forward calls to the chain extension.

You can access the auditing agency’s security assessment for Pendulum’s wrapper contracts by clicking here.

ERC20 Wrapper

The ERC20 wrapper contract implements OpenZeppelin’s [IERC20] interfaces. The purpose of this contract is to enable our wrapped tokens to satisfy the commonly-used interface, so that other Solidity contracts can use them in the same way as any other IERC20 token. It uses Solang's chain_extension function to communicate with the Tokens pallet of the Pendulum runtime.

In addition to the standard name and symbol arguments of the ERC20 constructor, this contract's constructor defines extra arguments that are used to derive the on-chain CurrencyId of the asset it is associated with. The variant and index parameters are mapped to the variants of the CurrencyId enum. More information about this derivation process can be found here.

Price Oracle Wrapper

The Price Oracle wrapper contract is a Solidity smart contract that implements the IPriceOracleGetter interface used in the Aave liquidity protocol. It leverages the chain extensions exposed by the Pendulum runtimes to obtain on-chain price data.

To map prices to smart contract assets, the price oracle wrapper must know which smart contract (ERC20) address corresponds to which asset. On Pendulum, asset price information is defined by combinations of blockchain and symbol identifiers. Therefore, the wrapper contract needs a mapping of address → (blockchain, symbol) to access runtime price information. OracleKey objects group all three of these parameters.

This contract enables querying price information for multiple assets using a single instance. To achieve this, the constructor of the contract takes a vector of OracleKeys as input. When a smart contract needs the price information for an asset at a given address, it can query an instance of the price oracle wrapper contract with that address. If the wrapper has an entry for that address in its OracleKeys, it will retrieve the asset price from the chain.

Bringing it Together

In the following, we want to connect the dots and show how the introduced components play together to bring existing DeFi projects to life on Pendulum. We consider the scenario of a ‘user smart contract’ (which is part of a DeFi project and implemented in Solidity) that needs to manage assets and needs access to price information.

Since the contracts pallet cannot execute Solidity bytecode, we must first compile our user and wrapper smart contracts to a specific Wasm format using Solang. Before the user smart contract can interact with our runtime data, we need to deploy the required instances of our wrapper contracts.

These instances target specific assets; each asset and price requires a separate instance of a wrapper contract. For example, to make the asset and price information of the USDC token available to smart contracts, we must first deploy an instance of the ERC20 and price oracle wrapper, specifying the respective details for the USDC token in the constructor calls.

Afterwards, we can deploy an instance of the user smart contract and pass references to the wrapper contracts to it. It will then be able to interact with these contracts without needing to know that they delegate the calls to the runtime pallets.

Importance for the Ecosystem

The approach described in the post can enable developers to write smart contracts once and deploy them on different networks without having to write different codes for each network. This can save time and resources for developers and promote interoperability between different blockchain networks.

DeFi projects that require the interfaces implemented by our wrapper contracts can be deployed without modification. This is a significant achievement as it not only saves time but also reduces costs that would otherwise be required for auditing agencies, among other things.

However, there are still some challenges to developing smart contracts on Polkadot. Unlike Ethereum, which has mature tooling for Solidity contracts, Polkadot’s tooling is still lacking. For instance, deploying a full suite of smart contracts with a given order and references between instances can be very tedious when done manually. Development environments like Hardhat can automate these processes on Ethereum. To simplify the deployment of Solang-compiled contracts on Pendulum, we are building the Wasm-deploy tool that can orchestrate these deployment processes. Keep your eyes peeled for the next technical Pendulum blog post that will deep dive into this Wasm-deploy tool!

About Amplitude

Pioneering the internet of fiat. Amplitude is the sister network of Pendulum on Kusama. It will act as a testing ground for Pendulum applications and network parameters and be powered by the AMPE token.

About Pendulum

Building the missing link between fiat and DeFi through a fiat-optimized smart contract blockchain based on Polkadot’s Substrate. Allowing traditional finance fiat services to integrate with DeFi applications such as specialized forex AMMs, lending protocols, or yield farming opportunities. Developed by SatoshiPay.

Keep your eyes on the Pendulum!

Website | Twitter | Telegram Announcements | Telegram Community | Discord | Docs

--

--

Pendulum
Pendulum-Chain

Traditional finance infrastructure blockchain. The missing link between fiat and DeFi. Limitless fiat. Decentralized future.