Vyper is a smart contract language paradigm deriving from Python 3 syntax and conventions and targeting the Ethereum Virtual Machine (EVM).

The EVM is a simulated global singleton computer which runs parallel to the block-chained ledger on Ethereum, allowing for the construction of more complex transactions and conditional self-executing agreements encoded smart contract objects.

The Ethereum platform itself is featureless and value agnostic, providing only the backbone of how smart contracts are to be put together and in the context of what applications.

Vyper is intended to be leveraged with the upcoming transition to Proof-of-Stake (Casper) and provide a more pragmatic minimalist regime for reading and writing of smart contracts, with focus on auditability, syntactical simplicity and straightforwardness.

Vyper vs. Solidity

Solidity Logo

In that, Vyper deviates sharply from the de facto mainstream Solidity. Since on-chain computations are resource constrained, they should be as strictly defined within the bare minimum necessity of their intended function, and Vyper takes this reductive approach to smart contracts, framing them as easily readable user roles and stories while leaving almost everything else out.

An immediately noticeable departure from Solidity is the doing away with inheritance so as to keep things “on the same page” rather than getting lost in jumping between multiple contract files in the hierarchy of their precedence in order to piece together the scattered fragments of what a program is doing under the hood.

Instead, emphasis is put on refined, stripped down composition and modularity (with types like owner, maintainer, token, deadline and possible expressions like “while gas left”) without, at the same time, relaxing any security assumptions, but rather enforcing syntactical transparency of making things immediately obvious and easily auditable, in line with the nature of contracts and distributed ledgers.

Security Features

Security is paramount in the context of smart contracts in a consensus integrated, globally distributed environment intended to function as a transparent notary and generalized institutional agency for writing trustless business logic.

In line with these goals, Vyper focuses on clarity of expression, rigorous clear-cut unambiguity and strong typing, and as such does away with operator overloading, trying to be as non-fragmented and articulate as possible (focus on the strictly necessary) in order to make it hard to write misleading code. In fact, it deliberately forbids some things in order to make them harder with the goal of increasing smart contract security by way of enforcing obvious, self-explanatory code patterns.

Recursive callings and infinite length loops are also excluded as opening the possibility for gas limit attacks and instead, Vyper aims to optimize gas metrics by estimating precise upper bounds for the gas consumption of any function call. Bounds and overflow checking for array accesses and arithmetic operations are included (no SafeMath library necessary) and no modifiers or constants are allowed to change the state.

Compiler Internals and Code Syntax

Vyper tries to stick to syntactical conventions close to the core of what they are describing, namely the EVM. Vyper scripts compile directly to EVM bytecode rather than get interpreted, unusual way of thinking about Python as this may be. Under the hood, both Vyper and Solidity compile to bytecode in the same fashion and sequence of steps, so they are largely inter-operable (and able to make external calls between each other’s contracts).

In brief, the code is taken up by a parser which disassembles it into an abstract syntax tree representation of the instructions, and from there a type checking process iterates through the tree assigning their corresponding types from bottom upward. After performing static analysis checks the bytecode is generated.

General Structure of a Contract

Vyper is feature-wise complete and presently awaiting audits and beta testing. Naming conventions in Vyper try to be as close to the core of what that code is trying to describe (i.e., the EVM, which is as simple as the bare minimum of what can be called a processor) as possible, although in a pythonesque kind of way.

The two types of integers are denoted as unit256 and int128 which respectively stand for non-negative and signed integers. unit256 is not fully supported as a numeric type due to complexity increase as most applications require just int128. unit256 has been included for ensuring interoperability with the ERC-20 standard.

An Ethereum smart contract usually consists of state variables and functions.

State variables are values which are permanently stored in contract storage and can be of number, string, address or boolean true/false expression types.

State variables are declared simply:

storedData: int256

Mappings are state variables which define sets of keys and corresponding values. In Vyper they are defined and accessed thus:

plainMapping: decimal[int256]
plainMapping[0] = 10.1

First the value is declared and then the type. In accessing a mapping, the position in the table is specified in square brackets.

Functions are the executable units of code within a contract which define the kind of behavior they can trigger. Similar to Python, functions in Vyper are declared with “def“.

Functions in smart contracts are either read functions (which are fast and don’t cost gas) or write/execute functions (which inscribe on the blockchain and therefore cost gas per cycle, and actualize in the next block).

A constructor function, which by Solidity convention has the same name as the contract, instantiates the given contract and its basic parameters on the blockchain as such. This function is executed only once and in Vyper it takes the form of the Python __init__ method (a special method called whenever an object of that class is created). For example, in a simple token contract:

@public
def __init__(_name: bytes32, _symbol: bytes32, _decimals: uint256, _initialSupply: uint256):

    self.name = _name
    self.symbol = _symbol
    self.decimals = _decimals
    self.totalSupply = uint256_mul(_initialSupply, uint256_exp(convert(10, 'uint256'), _decimals))

The self method explicitly asserts the particular instance variables of its class for semantic clarity.

Depending on their level of visibility , functions may be decorated with either @public or (by default) @private. Public visibility means that the method is exposed in the contract’s ABI (Application Binary Interface) which allows for external actors to call it.

In the above example, a constructor function instantiates the basic variables describing a token contract, i.e. name, ticker, decimal points of divisibility and total supply of minted tokens in circulation.

Other decorators include @constant, for decorating methods which only read a state, and @payable for designating methods which can be called with a payment.

For example:

@public
@payable
def bid(): // Function

External calls are supported via defining the external contract’s ABI at the top of the contract:

class Foo():
    foo(bytes32): pass

Events can be logged in indexed structures allowing clients to search for them.

Payment: __log__({amount: int128, arg2: indexed(address)})

total_paid: int128

@public
def pay():
    self.total_paid += msg.value
    log.Payment(msg.value, msg.sender)

Events must be declared before global declarations and function definitions.

Setting up the Environment

In Ubuntu, installing vyper as a ‘snap’ is a quick way to get going if having problems running Python 3.6:

$ sudo apt-get install snapd
$ sudo snap install vyper --edge --jailmode

Compiling a contract to bytecode is as straightforward as:vyper filename.v.py(file extension generally meant as .vy, but presently keeping .v.py for Python syntax highlighting)

To get the ABI:

viper -f json example.vy

Alternatively, an integrated online compiler is provided at vyper online which also features a rich set of examples such as a Solidity-compatible ERC-20 token, a financial events logger and an on-chain market maker. Unlike Remix for Solidity however, it doesn’t come with a test execution platform, but only compiles to bytecode and gives the ABI of the contract.

For testing contracts we need to spin a local blockchain environment, for the purposes of which Ganache (formerly TestRPC) from the Truffle suite is an option, but migrations need to be done manually from the console.

An alternative is running and can be run with the Parity client in private chain mode for which a pre-configured Docker image (with a single-node Proof-of-Authority blockchain) is provided here. Once the container is running, the graphical user interface can be accessed from the browser at localhost:8180. The browser based interface allows for deploying and interacting with accounts and contracts locally.

However, pyethereum (Ethereum core library for Python) is mostly used at the moment due to it being lightweight and overall Python compatible, with a plugin for natively testing contracts in development.

Development and Involvements

Despite lack of much publicity or mainstream attention, or even much documentation until very recently, Vyper has quietly been worked upon for quite some time now and is only lately.

However, it has begun to attract the attention of smart contract developers and security auditors from OpenZeppelin, and a contingent of people unhappy with the short comings of Solidity looking for more intuitively simple and less swiss army knife like alternatives.

Posted by Martin Banov

Martin Banov is a nerd of various stripes that seeks to cultivate a more nuanced, trans-disciplinary perspective with the underlying assumption that a chicken is just an egg's way of making more eggs. Hayekian in the economics department, Deleuzian in the philosophy wing and McLuhanist in his reasoning about technology.