These are your first steps... Working with Vyper

This week, I began the fun trip down the path of learning the new Ethereum language Vyper.

This week, I began the fun trip down the path of learning the new Ethereum language Vyper.  Since there is not a lot of good documentation out there on the subject, I figured I'd write up some of my experiences with the language so far.

As a Python developer I really wanted to work with Serpent a while ago, but that was found to have some serious bugs and so I waited for something new to come along.  In the mean time I learned Solidity as that is what every good Ethereum Blockchain developer learns these days.  Now that Vyper is entering a more stable phase I really want to roll up my sleeves and get my hands dirty with this new language.

What is Vyper

Vyper is a smart contract development language built with the following goals:

  • Security - it should be possible and natural to build secure smart contracts in Vyper.
  • Language and compiler simplicity - the language and the compiler implementation should strive to be simple.
  • Auditability - Vyper code should be maximally human-readable. Furthermore, it should be maximally difficult to write misleading code. Simplicity for the reader is more important than simplicity for the writer, and simplicity for readers with low prior experience with Vyper (and low prior experience with programming in general) is particularly important.

The toolset for the language is not as robust as Solidity.  That is where my work on MochaMousse may end up being used.  Linking a Python toolset with a python-ish smart contract language seems pretty cool to me.  

Links to docs and helpers

The project repo: LINK

The main docs:  LINK

The online compiler: LINK

Finally, the docs for web3.py: LINK

The Start

First you will need to install the compiler and other tools on your dev machine.  If you followed my Setting up a new MacBook for Ethereum Development post, you should be most of the way done as we installed Python in that post.  All you really need now is a virtualenv and then pip install vyper.

virtualenv -p python3.6 --no-site-packages ~/vyper-venv
source ~/vyper-venv/bin/activate
pip install vyper

After you have Vyper installed it is time to write your first smart contract.  Vyper is a bit different and has its own odd behaviors.  Coming from Solidity makes it feel like you are riding someone else's bicycle.  It's not hard, just a bit off. Coming from Python makes it seem a lot harder, as you need to get used to a strongly typed language.

The best quote describing it is from the repo:

Vyper does NOT strive to be a 100% replacement for everything that can be done in Solidity; it will deliberately forbid things or make things harder if it deems fit to do so for the goal of increasing security.

Automagically compiling contracts and deploying them takes a bit more effort as those tools have yet to be written.  Populus is supposed to have support, but I did not try to work with it as I wanted to focus on Vyper first.  So I ended up writing my own deploy and run scripts.  

The Test Contract

Change the Number

# Vyper Testing
# Change the number

stored_data: public(uint256)

@public
def change_number(_num: uint256):
    self.stored_data = _num

If you've been around the Ethereum world you would have come across the simple storage contract.  I wanted to do something along the same lines, just with numbers.  This contract is really simple, change and read a number in the stored_data state variable.  

The things that hung me up were that self.stored_data call you see in the change_number function.  For the longest time I was getting the following error:

vyper.exceptions.VariableDeclarationException: line 8: Variable type not defined
    stored_data = _num
----^

Finally after looking at the examples in the docs I realized that this is like working with classes in Python and I needed to add the self. in front of the state variable.  

Another thing that held me up was the way that I needed to declare state variables.  It is the opposite from Solidity uint256 public stored_data, in Vyper you need to write it like this:  stored_data: public(uint256). Like I said, not hard, just a different bicycle.

Vyper comes with some nifty exports when you run it.  By default it spits out compiled bytecode. Run the following to get the data you will need for working with the smart contract.

To output your ABI: vyper -f abi test.vy > test_abi.json

To output your bytecode to a file:  vyper -f bytecode test.vy > test_bytecode.bin

In the repo you will see a test_deploy.py and a test_run.py.  Both scripts are needed.  Run the deploy first against your Ganache local dev blockchain in order to make a nice artifacts file test_out.json, and then deploy the contract.  Then I used the test_run.py script to interact with it.  That script will prompt you for a number, change the number in the state, then repeat it back to you from the blockchain.  Sorry, no fancy ReactJS frontend as this is all just testing.

Putting it all together the commands look something like this:

(vyper-venv) computer:vyper_testing jeremy$ vyper -f abi test.vy > test_abi.json 
(vyper-venv) computer:vyper_testing jeremy$ vyper -f bytecode test.vy > test_bytecode.bin
(vyper-venv) computer:vyper_testing jeremy$ python test_deploy.py 
Deployed to: 0x6e7748a9e473d4Cea89deAD21428959b41939055

FIN
(vyper-venv) computer:vyper_testing jeremy$ python test_run.py 
The original number: 0
Enter new number: 42
Verified the NEW number: 42
FIN

The FizzBuzz contract

For 'fun' I added a FizzBuzz contract to 1) see if I could do it and 2) work with transaction logging.

The original Python FizzBuzz code looks something like this:

def fb(n):
    if n % 3 == 0 and n % 5 == 0:
        print('fizzbuzz')
    elif n % 3 == 0:
        print('fizz')
    elif n % 5 == 0:
        print('buzz')
    else:
        print(n)


for n in range(1, 16):
    fb(n)

You may recognize this if you've gone to a developer interview where they have you whiteboard out this problem.  

The output looks something like this:

(vyper-venv) computer:vyper_testing jeremy$ python fizzbuzz_original.py 
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
(vyper-venv) computer:vyper_testing jeremy$ 

I wanted to get the same output from the transaction logs.

Here's what I came up with:

# fizzbuzz.vy

# Events of the fizz n buzz

FizzBuzz: event({_value: int128})

# The FizzBuzz function
@public
def fb(n: int128):
    if n % 3 == 0 and n % 5 == 0:
        log.FizzBuzz(-3)
    elif n % 3 == 0:
        log.FizzBuzz(-2)
    elif n % 5 == 0:
        log.FizzBuzz(-1)
    else:
        log.FizzBuzz(n)

# Call the FizzBuzz function in the range from 1 to 15
@public
def doit():
    for n in range(1, 1 + 15):
        self.fb(n)

You will notice that when I log out, my values are either -3, -2, -1, or the argument number.  I ended up doing this to get around the PIA that built in function convert() is in the Vyper language.  You have to be really specific on your conversions to either decimal, int128, uint256 or bytes32.  I ended up writing a if conditional in my fizzbuzz_deploy.py script to output the correct values.

The flow is the same as the test contract, just no input is needed. Here is the output from a session:

(vyper-venv) computer:vyper_testing jeremy$ vyper -f abi fizzbuzz.vy > fb_abi.json 
(vyper-venv) computer:vyper_testing jeremy$ vyper -f bytecode fizzbuzz.vy > fb_bytecode.bin
(vyper-venv) computer:vyper_testing jeremy$ python fizzbuzz_deploy.py 
Deployed to: 0xaC54c06de6742928d2eA0F017fc8eD5f58a629Bf

FIN
(vyper-venv) computer:vyper_testing jeremy$ python fizzbuzz_run.py 
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
FIN

Wrapping up

So far I've learned a bunch about the new language and am looking forward to using it more regularly.  I really liked that I could the some Python code and tweak it a bit to be a smart contract.  That has some potential down the line.

In the future, I would like to re-write my business card dapp, and veloledger in Vyper, just to see it done.  I also want to integrate it into MochaMousse as I think it would be great to stay in a a Python mindset the entire time while developing Dapps.