Aleph Zero Blog
Technology Tutorials

Enabling Solidity on Aleph Zero – here’s how to get started!

Mar 21, 2024

While Solidity is the language used on EVM-compatible blockchains, it can also be utilized for smart contract development on Aleph Zero, opening up the network for the largest base of web3 developers. Let’s dive in!

The default language for writing smart contracts on Aleph Zero is ink!: a modern eDSL built on top of Rust. It offers several benefits, including a safe, strong type system and great memory management. However, a lot of developers coming from the EVM world are used to writing code in Solidity, the smart contract language used on EVM-compatible blockchains and by far the most popular one at that.

But what if we told you that as a Solidity developer, you can still use Aleph Zero as the infrastructure for your app and benefit from its fast finality and upcoming privacy features? Enter Solang, a new way to compile and deploy a Solidity contract on the Aleph Zero network. Here’s your guide on how you can use it!

Getting started with Solang

Solang is a tool that facilitates cross-compiling Solidity programs to be run on Substrate-based blockchains and Solana. It takes a Solidity source but instead of producing the EVM bytecode, it outputs WASM that will be understood by Aleph Zero’s pallet contracts. This is an incredible feature: devs can still write code using the language they are used to but run it on a WASM-based blockchain. Granted, Solang is still under development so not all Solidity code can be automatically translated, however, there is a lot of ongoing work that strives to achieve full compatibility.

Installing Solang

For Mac users, the task is very simple, all you have to do is use brew to install the solang binary:

brew install hyperledger/solang/solang

Linux users can either download pre-compiled binaries or use other installation methods as described in Solang documentation.

Writing your first Solidity contract

Let’s first test if we can run the most basic Solidity contract: the flipper. The flipper’s role is to hold a single boolean variable inside its state and flip it to its negation upon request. This is what the code looks like:

contract flipper {
        bool private value;

        constructor(bool initvalue) {
                value = initvalue;
        }

        function flip() public {
                value = !value;
        }

        function get() public view returns (bool) {
                return value;
        }
}

We can now use Solang to compile it to WASM (strictly speaking, Solang will compile it to WASM and then bundle it together with the metadata, additionally returning a *.contract file).

solang compile --target polkadot flipper.sol

After running this, we can take the flipper.contract file and upload it to an Aleph Zero chain: a local node or even Testnet. Try it out for yourself!

After we’re done with the very basic example, it’s time to do something way cooler!

Coin contract

Let’s now try to run a code of a very basic fungible token. This contract is taken directly from the Solidity documentation and it defines a coin that allows every user to transfer the coins they own and one user (minter) to create new coins. The code looks like this:

contract Coin {
    address public minter;
    mapping(address => uint) public balances;

    event Sent(address from, address to, uint amount);

    constructor() {
        minter = msg.sender;
    }

    function mint(address receiver, uint amount) public {
        require(msg.sender == minter);
        balances[receiver] += amount;
    }

    error InsufficientBalance(uint requested, uint available);

    function send(address receiver, uint amount) public {
        if (amount > balances[msg.sender])
            revert InsufficientBalance({
                requested: amount,
                available: balances[msg.sender]
            });

        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}

One notable change we’re introducing is the removal of the Solidity version pragma. This is a result of an approach adopted by Solang’s authors:

  1. ​​Solang tries to remain compatible with different versions of ethereum solidity; we cannot publish a version of solang for every version of the ethereum solidity compiler.
  2. We also have compatibility issues because we target multiple blockchains, so the version would not be sufficient.
  3. We think that the compiler version should not be a property of the source, but of the build environment. No other language sets the compiler version in the source code.

We can save this file as coin.sol and compile it in a similar way as before:

solang compile --target polkadot coin.sol

You can deploy this code right now to verify it works but we’ve got more things in store: using OpenZeppelin!

Using external libraries

Using libraries is a cornerstone of good development practices that saves time, lets you avoid repeating others’ work and contributes to standardization. In the EVM world, the de-facto standard library is OpenZeppelin, which contains common token contracts and a plethora of very useful utilities for contract creation.

In order to move forward with this step, we will need to have OpenZeppelin contracts handy in some local folder. The recommended way of obtaining them is via npm (you’ll need to have it installed). We’ll initialize an empty node project in the folder where we’re storing our contracts and install the OpenZeppelin library:

npm init
npm install @openzeppelin/contracts

Now, we can slightly improve our coin contract by replacing the manual check for the minter with the Ownable extension from OpenZeppelin. First, we’ll add an import declaration:

import "@openzeppelin/contracts/access/Ownable.sol";

Then, we’ll make our contract Ownable by changing the declaration to:

contract Coin is Ownable {  // …

Next comes the important part: normally in Solidity you could just use a default constructor of Ownable. However, Solang requires you to explicitly invoke the constructor of the parent class (Ownable):

constructor() Ownable(msg.sender) {  // …

And finally, to make sure that the mint function can only be called by the owner, we will change its signature to:

function send(address receiver, uint amount) public {

Additionally, now we can remove the minter variable and all its usages: it’s all handled by the Ownable extension.

Compiling the code with external libraries

In order to compile the code that relies on OpenZeppelin, we will need to tell Solang where to look for the libraries. What we can do is provide a mapping in form:

library_prefix=library/path

If Solang sees a library being imported, it will try to find its prefix in the mapping and replace the path. Assuming you installed the library in the current directory using npm, the command will look like this:

solang compile --importmap @openzeppelin=node_modules/@openzeppelin --target polkadot token.sol

And just like that, our first contract enhanced with OpenZeppelin is ready to be deployed to Aleph Zero! We can check if ownable works; below you see what happens if an account that’s not an owner tries to mint the coin: it’s not allowed, which means our access control works!

To make sure that the owner can perform an action, let’s try to mint using the contract owner’s account:

Again, it seems our contract works just fine! Of course, please remember that in real-life scenarios tests like that are definitely not enough: a contract needs to have a test suite.

Bonus: Deploying ERC20 token

Last but not least, let’s try to use the most popular token type from OpenZeppelin: ERC20. The code we will use is the following couple of lines:

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
   constructor(uint initialSupply) ERC20("MyToken", "MTKN") {
       _mint(msg.sender, initialSupply);
   }
}

Three things we need to remember are:

  • To import the correct token from OpenZeppelin;
  • To invoke its constructor with the correct parameters;
  • To mint the initial supply to the sender.

The compilation command is exactly the same as previously and should produce a valid .contract file. You can check how it works on chain!

Next steps

Please remember, that there is more to building dApps than just smart contracts: you need RPC calls, frontend libraries and much more. What we are showing here is just a first step in the direction of more interoperability and onboarding more developers. We hope you will have a great time experimenting with Solidity on Aleph Zero!