Deploying Solidity Smart Contracts to Solana

What is Solana and how can you deploy Solidity smart contracts to it?

Solana is a new blockchain focusing on performance. It supports smart contracts like Ethereum which they call Programs. You can develop those in Rust, but there's also a new project now to compile Solidity to Solana. In other words you can deploy your contracts written in Solidity now to Solana!

And of course the costs of transactions on Solana are a fraction of those in Ethereum. So how does it all work?


Solana Meme

Transaction Ordering (Proof of History)

The biggest feature of Solana is its Proof of History (PoH) which is based on a chain of sha256 hashes as proof of time being passed. The idea behind it is that to compute hash300, one has to sequentially calculate first hash1, then hash2 on so on. That's because the hash output cannot be predicted and each intermediate result is automatically the input for the next intermediate result.

The lastest CPU generations are extremely fast at calculating sha256, but again it has to be done sequentially. And that's why one can have certainty there won't be a custom ASIC which is 100x faster.

Proof of History Example 1
Proof of History Example 2

So when a node receives transactions signed with hash300, it will know those transactions are to be placed after hash200, but before hash400 (assuming 100 hashes as delay). This is quite similar to the concept of Verifiable Delay Functions (VDFs) which are also used for ETH2.0. The one difference lies in proof verification which for VDFs can be done in significantly less complex steps than proof creation, while for PoH requires every single hash to be calculated again. So how can PoH verification be done efficiently?

Luckily PoH proof verification, unlike the PoH proof creation, can be parallelized. The proof will have to contain every intermediate hash where then each intermediate hash calculation can be verified in parallel. This is possible very efficiently on modern GPUs. Of course the downside for this is very large proof sizes and generally high Solana validator hardware requirements. The upside is performance, because it reduces messaging overhead + latency due to providing a predetermined transaction order.

New transactions bundled in a batch and optimistically streamed via UDP from the current leader to all other validators where each validator receives a different data part of the bundle. In the next step validators are sharing the missing sets between each other, all of this happens concurrently, non-stop and streamed resulting in very high performance.

Consensus on Solana (Proof of Stake)

PoH doesn't solve the consensus problem though, for that Solana is using a version of PBFT (Practical Byzantine Fault Tolerance) which is similar to Cosmos' Tendermint consensus algorithm (here's a good video overview about it) called Tower BFT. But since Solana can use the PoH for its blockchain clock, the consensus timeouts of PBFT can be encoded with this directly.

Timeouts for all predecessor PBFT votes double with each new vote. Imagine a scenario where every validator has voted 32 times in the last 12 seconds. The last vote from 12 seconds ago now has a timeout of 2³² slots, or roughly 54 years in PoH time. Or in other words you would have to compute sha256 hashes on a CPU for 54 years to be able to rollback that vote.

Other features of Solana include:

  • Turbine — a block propagation protocol
  • Gulf Stream — Mempool-less transaction forwarding protocol
  • Sealevel — Parallel smart contracts run-time
  • Pipelining — a Transaction Processing Unit for validation optimization
  • Cloudbreak — Horizontally-Scaled Accounts Database
  • Replicators — Distributed ledger store

If you want to learn more, check out Solana's docs, whitepaper and blog posts.

Deploying ERC-20 Token to Solana

Deploying an ERC-20 to Solana from Solidity requires installing all tools and running a deploy script.

1. Installing Solang

The best way to install Solang is using the VS Code extension. It will automatically install the correct solang binary along with the dependencies. Alternatively you can download the binary directly and manually install the dependencies. The VS Code extension will also give you compiling capabilities for Solang, the regular Solidity extension wouldn't be quite accurate due to the differences in the supported features.

To make the extension work properly, you only need to more steps:

1. Make sure to choose Solana as target in the extension settings.

VS Code Solang Target

2. Disable the solidity extension for the workspace.

VS Code Solang Extensions

Now let's take an ERC20 contract, the code here is almost a 1:1 copy from Openzeppelin.

You'll also want to initialize the package and install required dependencies:

$ npm init
$ npm install @solana/solidity @solana/web3.js

2. Installing Solana Tool Suite

Next up install the Solana test suite. If you're running on Mac OS, it's as simple as running:

$ sh -c "$(curl -sSfL https://release.solana.com/v1.8.5/install)"

3. Create the ERC-20 contract

Now let's take an ERC20 contract as ERC20.sol in our package root, the code here is almost a 1:1 copy from Openzeppelin.

4. Compile Solidity -> Solana

Next up install the Solana test suite. If you're running on Mac OS, it's as simple as running:

$ solang ERC20.sol --target solana --output build

This will produce

  • build/ERC20.abi: Just as you know from Ethereum, the ABI for our contract is generated.
  • build/bundle.so: New here is the compiled Solana Program.

5. Deploy the ERC-20 contract

Now create the following deploy-erc20.js script:

const { Connection, LAMPORTS_PER_SOL, Keypair } = require('@solana/web3.js')
const { Contract, publicKeyToHex } = require('@solana/solidity')
const { readFileSync } = require('fs')

const ERC20_ABI = JSON.parse(readFileSync('./build/ERC20.abi', 'utf8'))
const BUNDLE_SO = readFileSync('./build/bundle.so')

;(async function () {
    console.log('Connecting to your local Solana node ...')
    const connection = new Connection(
        // works only for localhost at the time of writing
        // see https://github.com/solana-labs/solana-solidity.js/issues/8
        'http://localhost:8899', // "https://api.devnet.solana.com",
        'confirmed'
    )

    const payer = Keypair.generate()
    while (true) {
        console.log('Airdropping (from faucet) SOL to a new wallet ...')
        await connection.requestAirdrop(payer.publicKey, 1 * LAMPORTS_PER_SOL)
        await new Promise((resolve) => setTimeout(resolve, 1000))
        if (await connection.getBalance(payer.publicKey)) break
    }

    const address = publicKeyToHex(payer.publicKey)
    const program = Keypair.generate()
    const storage = Keypair.generate()

    const contract = new Contract(connection, program.publicKey, storage.publicKey, ERC20_ABI, payer)

    console.log('Deploying the Solang-compiled ERC20 program ...')
    await contract.load(program, BUNDLE_SO)

    console.log('Program deployment finished, deploying ERC20 ...')
    await contract.deploy('ERC20', ['MyToken', 'MTO', '1000000000000000000'], program, storage, 4096 * 8)

    console.log('Contract deployment finished, invoking contract functions ...')
    const symbol = await contract.symbol()
    const balance = await contract.balanceOf(address)

    console.log(`ERC20 contract for ${symbol} deployed!`)
    console.log(`Wallet at ${address} has a balance of ${balance}.`)

    contract.addEventListener(function (event) {
        console.log(`${event.name} event emitted!`)
        console.log(
            `${event.args[0]} sent ${event.args[2]} tokens to
       ${event.args[1]}`
        )
    })

    console.log('Sending tokens will emit a "Transfer" event ...')
    const recipient = Keypair.generate()
    await contract.transfer(publicKeyToHex(recipient.publicKey), '1000000000000000000')

    process.exit(0)
})()

Here we are making use of


If you are Dapp developer and want to connect a wallet, have a look at Solana wallet adapter instead.

Now we're ready to run your own local Solana chain:

$ solana-test-validator --reset --quiet

And in a separate tab run our script:

$ node deploy-erc20.js

We just deployed the ERC-20 token to our local Solana chain! 🎉🎉🎉

So what exactly is Solang?

In their own words: With Solang you can compile smart contracts written in Solidity for Solana, Parity Substrate, and Ethereum ewasm. It uses the llvm compiler framework to produce WebAssembly (wasm) or BPF contract code.

How does it compare to projects cloning the EVM like Moonbeam and Evmos? Since EVM cloning keeps all the overhead from running the EVM, the solution with Solang should scale much more efficiently since it's running natively on the chain, but there are some caveats to it:

Solang aims to be compatible with Solidity 0.7, however there are some key differences:

  • Libraries are always statically linked into the contract code.
  • Solang generates WebAssembly or BPF rather than EVM. This means that the assembly {} statement using EVM instructions is not supported.
  • No gas exists for Solana.  There is compute budget which may not be exceeded, but there is no charge based on compute units used.
    • The gas cannot be set on Solana for external calls.
    • tx.gasprice is not available.
    • gasleft() is not available.
  • block.number gives the slot number rather than the block height.
  • You can print output for debugging using print().
  • selfdestruct and tx.origin are not available.
  • Solana addresses are base58 encoded, not hexadecimal. An address literal can be specified with the special syntax address"<account>".
    address foo = address"5GBWmgdFAMqm8ZgAHGobqDqX6tjLxJhv53ygjNtaaAn3sjeZ";
  • The size of the new account can be specified using space. By default, the new account is created with a size of 1 kilobyte (1024 bytes) plus the size required for any fixed-size fields. When you specify space, this is the space in addition to the fixed-size fields (like strings in the state). So, if you specify space: 0, then there is no space for any dynamicially allocated fields.
contract Hatchling {
    string name;

    constructor(string id) payable {
        require(id != "", "name must be provided");
        name = id;
    }
}

contract Adult {
    function test() public {
        Hatchling h = new Hatchling{space: 10240}("luna");
    }
}
  • So is this ready to use?
    • I'm not sure if it's production ready, but even if not, it's surely getting there soon.
  • Is our compiled ERC-20 contract compatible with other SPL tokens on Solana?
    • Also not sure, if you know the answer, please leave a comment.

Markus Waas

Solidity Developer

More great blog posts from Markus Waas

© 2024 Solidity Dev Studio. All rights reserved.

This website is powered by Scrivito, the next generation React CMS.