DEV Community

Cover image for 
Programming an Ethereum Smart Contract with Vyper
librehash
librehash

Posted on

Programming an Ethereum Smart Contract with Vyper

Please Note: This guide was crafted from an already existing boilerplate. The original goal was to take that boilerplate and save it as a personal guide. However, after a brief look through, I quickly noticed there were a ton of errors (no explanations + inefficient coding). So, this is essentially a re-constructed guide. Feel free to share anywhere; this is copyleft #libre.

Ethereum has a vast amount of developer resources available, such that for a beginner its hard to know where to start.

If you go with the recommended approach you'd probably choose Solidity along with either JavaScript or golang. JavaScript and golang are traditionally the most well supported languages for the developer tooling around Ethereum. The original Ethereum node software, geth, is written in golang. There's a version of the Solidity smart contract compiler, solcjs, written in JavaScript, and available as a Node.js NPM package.

The other Smart Contract language that is supported by the Ethereum Foundation is Vyper. Vyper is a python-like language that was designed for security, simplicity and testability. In my view this makes Vyper ideal for a beginner. Vyper foregoes some of the power of Solidity (for example, class inheritance and function modifiers) in order to uphold the Vyper principles of being simple and secure.

In this article I'll be stepping you through creating a smart contract with the Vyper programming language, deploying it to the Ethereum test network, and then interacting with the contract - calling its two externally accessible functions. The contract deployment and contract interaction are achieved using two in-browser tools - MyEtherWallet (MEW) and MetaMask.

Installing the Vyper compiler

There's a number of options available to you as documented here:

https://vyper.readthedocs.io/en/latest/installing-vyper.html

One of the simplest options is to install it using python's package manager (pip). I'll start by ensuring you have the python development environment setup correctly.

This Process Involves the Following

  1. Installing a recent version of Python and Pip with Pyenv

  2. If you don't have already have a recent version of Python installed, or don't even know, I would highly recommend that you start by installing pyenv. Pyenv is a tool for python version management.

  3. I followed the Pyenv installation instructions for a MacOS installation via homebrew, but there are instructions for other OSes given.

After Completing the Outlined Steps Above, Proceed With the Following

Install the latest version of Python3

$ pyenv install 3.x.x

(check which version is the latest on their site: https://www.python.org/downloads/)

You will now have an up to date version of both python and pip available from your terminal command prompt.

Check what version of python is installed:

$ python -V  
Enter fullscreen mode Exit fullscreen mode

Output:

Python 3.8.0

Check what version of pip is installed:

$ pip -V

Output:

pip 19.2.3 from /Users/hg/.pyenv/versions/3.8.0/lib/python3.8/site-packages/pip (python 3.8)

Installing the Vyper Compiler

Once you have python and pip installed aready, you can install the Vyper compiler with:

$ pip install vyper

Check what version of Vyper is installed:

$ vyper --version

Writing a smart contract in Vyper

For the first contract, we're going to keep things very simple and go with a public storage contract; all the contract does is stores a single number in its storage area, and implements two public accessor functions - a getter and a setter.

This section will outline the requisite steps for doing so:

  1. Use a text editor and open a new file named storage.vy

  2. Note that vy is the file extension for Vyper source code.

Below is the relevant Vyper code:

stored_data: uint256  

@public  
def set(new_value : uint256):  
self.stored_data = new_value  

@public  
@constant  
def get() -> uint256:  
return self.stored_data  
Enter fullscreen mode Exit fullscreen mode

Breaking Down the Code Above:

  • Line 1 declares a variable named 'stored_data' of type 'uint256'. This is the only thing that the contract will store on the blockchain.

  • Line 3 (def set...) declares the public set function, to update the 'stored_data' variable.

  • Line 9 (def get...) declares the public get function, to get the 'stored_data' variable.

Its important to note that these two functions get and set are marked public and so can be 'called' by anyone - by using Ethereum client software like MyEtherWallet, or programmatically via the web3 or by other library.

Calling a public function on a smart contract actually consists of sending a transaction to the deployed contract's address - but this is an implementation detail hidden that is hidden by client software or libraries.

Further reading about the structure of contracts in Vyper:

https://vyper.readthedocs.io/en/latest/structure-of-a-contract.html

Compiling the Vyper smart contract

To compile the contract into EVM bytecode:

$ vyper -f bytecode storage.vy > storage.bin

To generate the ABI for the contract:

$ vyper -f abi storage.vy > storage.abi

The ABI is a JSON format interface specification file, and it is needed to be able to interact with the smart contract (i.e. call its functions).

Deploy the smart contract to the Rinkeby test network

Install Metamask

  1. If you haven't already, now is a good time to install the MetaMask plugin/extension into your browser - https://metamask.io/

    Setup MetaMask with a seed phrase and it will create an in-browser Ethereum wallet that you can use for real transactions, or in this case, test transactions.

  2. When MetaMask is setup, at the top right you should see the network dropdown.

  3. Change this to 'Rinkeby Test Network'. Press the DEPOSIT button, then press the GET ETHER button under the TEST FAUCET option.

  4. After opening https://www.rinkeby.io/#stats use the 'Crypto Faucet' button on the left and follow the instructions. The minimum option (3 ETH) will be more than enough for test purposes and to deploy this contract.

Metamask Wallet Setup: The second thing you'll need to do is to setup MyEtherWallet (MEW). This will let you deploy and interact with your smart contract.

  1. Setup MyEtherWallet (MEW)

  2. Go to https://www.myetherwallet.com and setup the seed phrase.

  3. Now you'll need to give MyEtherWallet (MEW) access to your MetaMask wallet, which contains the 3 ETH for testing.

  4. Go to https://www.myetherwallet.com/access-my-wallet and press the browser extension option.

  5. It should come up with a 'Access via MetaMask' window, and you'll need to grant it permission. Press the 'Access My Wallet' button.

  6. Deploy the smart contract

  7. Inside MyEtherWallet (MEW), press the left hand side option 'Contract' then 'Deploy Contract'.

From the terminal, issue this command:

$ cat storage.bin

Which Should Render the Following Output (Hexadecimal):

0x6100f456600436101561000d576100ea565b600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a0526360fe47b160005114156100c25734156100ba57600080fd5b600435600055005b636d4ce63c60005114156100e95734156100db57600080fd5b60005460005260206000f350005b5b60006000fd5b6100046100f4036100046000396100046100f4036000f3  
Enter fullscreen mode Exit fullscreen mode

After output is generated, copy said output to clipboard

quick note: could pipe this to your clipboard in one command by running some variant of

cat storage.bin | xclip -sel clip -rmlastnl

Inside MEW, paste the copied text contents into the bytecode field.

Go back to the terminal and run the following command:

$ cat storage.abi | xclip -sel clip -rmlastnl

It should produce an ABI output like the following:

[{"name": "set", "outputs": [], "inputs": [{"type": "uint256", "name": "new_value"}], "constant": false, "payable": false, "type": "function", "gas": 35315}, {"name": "get", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 1181}]  
Enter fullscreen mode Exit fullscreen mode

For demonstration / display purposes, I'm going to prettify the format in JSON before putting in the Markdown codebox.

[
  {
    "name": "set",
    "outputs": [],
    "inputs": [
      {
        "type": "uint256",
        "name": "new_value"
      }
    ],
    "constant": false,
    "payable": false,
    "type": "function",
    "gas": 35315
  },
  {
    "name": "get",
    "outputs": [
      {
        "type": "uint256",
        "name": "out"
      }
    ],
    "inputs": [],
    "constant": true,
    "payable": false,
    "type": "function",
    "gas": 1181
  }
]
Enter fullscreen mode Exit fullscreen mode

Formatted in JSON because it was begging me to.

  • Inside MEW, paste the copied text contents into the ABI/JSON field.

  • Enter a name for your contract, e.g. 'storage1'.

  • Press the 'Sign Transaction' button.

  • MetaMask will open up a window where you'll be able to confirm the deployment of your contract.

  • You'll see the gas fee and total.

  • You can press on the data tab and you'll see the bytecode that you pasted earlier.

Press the Confirm button to proceed with the deployment of your contract.

You may need to wait several seconds for the transaction to be confirmed at this point.

MetaMask should give you a successful deployment popup and link you to the transaction on etherscan.io; https://rinkeby.etherscan.io/tx/0x36663b338ab0eaa7d7cdd91aa5abacdc273757ff56b81221d76a2ff0aedc9860

Here you can see the address of your newly deployed contract. In my case it is here:

https://rinkeby.etherscan.io/address/0x7baad2f634d6bde84916e7d6db40ca2e502eaff6

Additional Note on Ethereum Contract Addresses

Contract addresses are calculated deterministically, based on the account that created it. They are based on the nonce value of your account. So it is possible to know in advance what the contract address will be, if necessary.

Details here: https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed

Inserting My Opinion Here: That is extremely fucking interesting... (one could actually leverage this for some useful purpose in Ethereum if they cared; but they don't and the protocol is wholly centralized

Interacting with the smart contract

Now that your contract is deployed, its time to interact with it - call its get and set functions.

The get function can be called 'for free' - it doesn't cost any gas, because its just returning the current state of the blockchain.

Calling the 'set' fuction however must be paid for in gas, because by calling it you are actually changing the current state of the blockchain.

New Instructions

  1. Go back to MEW and navigate to the left hand side 'Contract' option then 'Interact with Contract'.

  2. Paste your newly created contract address into the 'Contract Address' field.

  3. In the ABI/JSON field, paste your ABI file contents.

  4. Press the Continue button.

  5. There's a dropdown which shows 'Select an item'. This lists all of the public functions on the smart contract. There's only two available - get and set.

  6. Choose get first, it will execute immediately and show the result being 0. 0 is the default value for uninitialised storage in Ethereum.

  7. Now choose 'set' from the dropdown.

  8. Enter the required value into the new_value field, e.g. 88.

  9. Leave the Value in ETH as 0.

  10. Press the 'Write' button.

Final Confirmation: MetaMask will open up a window where you'll be able to confirm your contract interaction; what you're actually doing here is sending a transaction to the contract with a value in the data field - the data specifies to the EVM which function is being called (set) and the value of the parameter to it (88).

  1. Press the confirm button.

  2. You may need to wait several seconds for the transaction to be confirmed.

  3. MetaMask will eventually TX confirmation via notification, with a link your newly generated transaction

  4. After clicking on the link, you should find yourself at the transaction details for the new contract you just minted on the Rinkeby protocol. Once there, scroll down to the bottom of the page and click the 'See More' button; if you're having trouble finding it, just hit (ctrl + f), then type: "See More" (without quotes)

  5. Go back to MEW and this time select the 'get' from the dropdown.

  6. Confirm that the value you set is now reflected by the get.

There were some additional notes that the author wrote; but probably not worth it to transcribe that as well

Top comments (4)

Collapse
 
dmitridon profile image
Dmitri Don

Would be interesting to see similar tutorial using Remix

Collapse
 
librehash profile image
librehash

Will keep in mind; many more ETH-related tutorials on the way soon

Collapse
 
thorstenhirsch profile image
Thorsten Hirsch

Uniswap switched from Vyper (Uniswap V1) to Solidity (Uniswap V2). Do you know why? Has Vyper failed?

Collapse
 
librehash profile image
librehash

Hey, good question. Did a little perusing and this blog explains it = uniswap.org/blog/uniswap-v2/

Looks like Solidity was just a better fit for their specific use case - but I didn't see anything from Uniswap or elsewhere that indicated there were any issues w Vyper.

I'll keep an eye out though!