An EVM low-level language that gives full control over the control flow of the smart contract.

Related tags

Cryptography meplang
Overview

Meplang - An EVM low-level language

Meplang is a low-level programming language that produces EVM bytecode. It is designed for developers who need full control over the control flow in their smart contracts.

Meplang is a low-level language and is not meant for complex smart contract development. It is recommended to use more high-level languages like Solidity or Vyper for that.

Please note that the work on Meplang is still in progress, and users should always verify that the output bytecode is as expected before deployment.

Installation

  1. Install Rust on your machine.

  2. Run the following to build the Meplang compiler from source:

cargo install --git https://github.com/meppent/meplang.git

To update from source, run the same command again.

Hello World

Here is an example of a simple Meplang contract, that returns "Hello World!" as bytes:

contract HelloWorld {
    block main {
        // copy the bytes into memory
        push(hello_world.size) push(hello_world.pc) push(0x) codecopy

        // return them
        push(hello_world.size) push(0x) return 
    }

    block hello_world {
        // "Hello World!" as bytes
        0x48656c6c6f20576f726c6421
    }
}

To compile this contract saved as hello_world.mep, run the following command:

meplang compile -contract HelloWorld -input hello_world.mep

Or the shortened version:

meplang compile -c HelloWorld -i hello_world.mep

This will print the runtime bytecode in the terminal. To save the output as in a file, use the argument -o or -output:

meplang compile -c HelloWorld -i hello_world.mep -o runtime.bytecode

Deployment bytecode

The compilation gives the runtime bytecode of the smart contract. To get the deployment contract, use an auxilliary contract, and compile it:

contract Constructor {
    block main {
        // copy the bytes into memory
        push(deployed.size) push(deployed.pc) push(0x) codecopy

        // return them
        push(deployed.size) push(0x) return 
    }

    block deployed {
        &Deployed.code
    }
}

// the contract that will be deployed
contract Deployed {
    block main {}
}

Compile the contract Constructor to get the deployment bytecode of the contract Deployed.

Basic syntax

  • A contract is declared with the keyword contract. Many contracts can be defined in a single file. A contract can copy the runtime bytecode of another contract using &Contract.code inside a block.
  • A block is declared inside a contract using the keyword block. A block can be defined abstract (see later) using the keyword abstract before block. The first opcodes of the contract are from the necessary block named main (or a block surrounded by the attribute #[main]).
  • A constant is declared inside a contract using the keyword const. Constants can only be used inside a function push inside a block.
contract Contract {
    const balance_of_selector = 0x70a08231;
    const weth_address = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    #[assume(msize = 0x00)]
    block main {
        push(balance_of_selector) push(0x) mstore // mem[0x1c..0x20] = 0x70a08231
        #[assume(msize = 0x20)]
        address push(0x20) mstore // mem[0x20..0x40] = address(this)
        #[assume(msize = 0x40)]
    
        // mem[0x00..0x20] = weth.call{value: 0, gas: gas()}(mem[0x1c..0x20])
        push(0x20) push(0x1c) push(0x24) push(0x) push(0x) push(weth_address) gas call

        // the contract's balance in weth is stored at mem[0x00..0x20]
    }
}
  • Inside a block, any opcode can be used except PUSH1 to PUSH32 opcodes. Raw bytecode can also be used as is. A value can be pushed using the function push, which can take an hexadecimal literal, a constant, a non-abstract block PC or size as an argument. Only values inside a push function will be optimized by the compiler.
contract Contract {
    const magic_number = 0xff;

    #[assume(msize = 0x00)]
    block main {
        push(magic_number) push(0x) mstore
        #[assume(msize = 0x20)]

        push(0x20) // can be replaced by the opcode `msize` during the compilation
        0x6020     // won't be changed at the compilation

        push(end_block.size) // will be replaced by the actual size of the block `end_block`
        push(end_block.pc)   // will be replaced by the actual pc of the beginning of the block `end_block`
        jump
    }

    block end_block {
        jumpdest // do not forget to begin with jumpdest if we can jump on this block
        push(0x) push(0x) return
    }
}
  • A non-abstract block can be copied at most once inside another block using the opreator *. An abstract block can be copied as many times as desired inside other blocks using the operator &. Therefore, we cannot refer to the pc or to the size of an abstract block, because it may appear multiple times in the bytecode, and not be compiled the same every time.
contract Contract {
    #[assume(msize = 0x00)]
    block main {
        callvalue &shift_right_20_bytes // will most certainly be compiled `callvalue push1 0x20 shr`
        push(0x) push(0x) mstore
        #[assume(msize = 0x20)]
        callvalue &shift_right_20_bytes // will most certainly be compiled `callvalue msize shr` because we assumed msize = 0x20.
        *end_block
    }

    abstract block shift_right_20_bytes {
        push(0x20) shr
    }

    block end_block {
        // no jumpdest here because we do not jump on this block, we copy it
        push(0x) push(0x) return
    } 
}
  • Many attributes exist to guide the compiler. They are declared over a contract, a block, or a line inside a block using the syntax #[ATTRIBUTE]. The current list of existing attributes is:
    • assume to tell the compiler that from this point, an opcode will push on the stack a defined value. The compiler can then replace some push opcodes with these assumptions.
    • clear_assume to clear an assumption made previously.
    • main the main block can be marked with this attribute if it is not named main.
    • last to tell the compiler that the block must be placed at the end of the bytecode.
    • keep to tell the compiler that this block must be kept somewhere in the bytecode even if it is unused.

More examples of contracts can be found in the folder examples.

Future features

  • assert attribute to impose conditions on a block pc or a contract size.
  • Heuristics to improve compilation optimizations.
  • Inheritance of contracts.
You might also like...
Simple template for building smart contract(Rust) and RPC Client(web3.js) on Solana (WIP) ⛏👷🚧⚠️
Simple template for building smart contract(Rust) and RPC Client(web3.js) on Solana (WIP) ⛏👷🚧⚠️

Solana BPF Boilerplate Simple template for building smart contract(Rust) and RPC Client(web3.js) on Solana This boilerplate provides the following. Si

Smart Contract for Terra Name Service

TERRA NAME SERVICE CONTRACTS Terra Name Service is to create easy-to-remember names for your Terra address like ‘dokwon.ust’ instead of ‘terra1...whez

Vectis - Smart Contract Wallet

A smart contract wallet project to add functionality for users of DApps to manage their keys by allowing for recovery and account freeze, whilst preserving user control, and enabling relayer capability for gas provisioning

Smart Contract built in Rust to run inside Cosmos SDK module on all chains that enable it

CoinSwap is a Smart Contract that is built on the terra blockchain and can be used to swap cryptocurrencies such as LUNA, UST, TerraUSD, Anchor, Mirror Protocol, LUNI and other CW20 tokens. The Project also contains a smart contract which works as a analysis tool for the gas fees on the Terra Blockchain.

Heimdall is an advanced Ethereum smart contract toolkit for forensic and heuristic analysis.
Heimdall is an advanced Ethereum smart contract toolkit for forensic and heuristic analysis.

Heimdall is an advanced EVM toolkit which aims to make dealing with smart contracts on EVM based chains easier. Installation & Usage Heimdall's update

A simple example demonstrating cross-contract calls in CosmWasm smart contracts

Cross-contract calls This tutorial demonstrates cross-contract calls with CosmWasm v1. Overview An end user calls the reservation contract to register

The Ink! smart contract SDK for XVM interface

Ink! XVM SDK The Ink! smart contract SDK for XVM interface. This SDK contains contract wrappers and all middleware code to make XVM development easy.

Example NFT marketplace project using ink! smart contract.

NFT Marketplace project This contract is an example for the NFT marketplace implementation. License Apache 2.0 🏗️ How to use - Contracts 💫 Build Use

MevWallet is a smart contract wallet that allows the user to capture MEV from Searchers, or create MEV on purpose.

MevWallet MevWallet is a smart contract wallet that allows the user to capture MEV from Searchers, or create MEV on purpose. This repo contains the so

Releases(v0.1.0)
Owner
MEP
MEP
Emerging smart contract language for the Ethereum blockchain.

Emerging smart contract language for the Ethereum blockchain.

null 1.4k Jan 9, 2023
Parser and test runner for testing compatable common Ethereum full node tests against Polygon Zero's EVM.

EVM Test Parses and runs compatible common Ethereum tests from ethereum/tests against Polygon Zero's EVM. Note: This repo is currently very early in d

Mir Protocol 3 Nov 4, 2022
A low-level assembly language for the Ethereum Virtual Machine built in blazing-fast pure rust.

huff-rs • huff-rs is a Huff compiler built in rust. What is a Huff? Huff is a low-level programming language designed for developing highly optimized

Huff 276 Dec 31, 2022
An example CosmWasm contract for connecting contracts over IBC.

CosmWasm IBC Example This is a simple IBC enabled CosmWasm smart contract. It expects to be deployed on two chains and, when prompted, will send messa

ekez 64 Jun 21, 2023
Substrate NFT !ink smart contract base

Substrate !ink NFT simple implementation This is a simple working version of base NFT smart contract written using latest (as of this date) !ink 3.0.0

POLK4.NET 14 Dec 3, 2022
clockchain is a system for benchmarking smart contract execution times across blockchains.

Clockchain Clockchain is a research tool for benchmarking smart contract execution times across blockchains using Arcesco-- a block-chain agnostic ins

Zeke Medley 7 Oct 3, 2022
Testing a smart contract on the Solana blockchain

Environment Setup Install Rust from https://rustup.rs/ Install Solana from https://docs.solana.com/cli/install-solana-cli-tools#use-solanas-install-to

Maurice 1 Oct 25, 2021
clockchain is a system for benchmarking smart contract execution times across blockchains.

Clockchain Clockchain is a research tool for benchmarking smart contract execution times across blockchains using Arcesco-- a block-chain agnostic ins

zeke 7 Oct 3, 2022
The NFT smart contract powering xyz on Terra

xyz NFT Contract This repository contains the core NFT smart contract that implements xyz, a base layer for metaverses on the Terra blockchain. The xy

null 16 Sep 25, 2022
An example smart contract that builds on top of xyz

xyz Guestbook Tutorial Contract This repository contains an example smart contract that illustrates how to build on top of the xyz NFT contract. This

null 5 Apr 4, 2022