Vyper for Beginners — Variables, Flow Control, Functions, Hello World!

Welcome back to Vyper for Beginners! This lesson covers the basic structure of a Vyper contract, a review of Vyper’s variable types, Vyper’s built-in flow control logic (including how it differs from Python), and wraps up with a simple “Hello World” contract that you can deploy on a test net or a local fork.

This builds off the previous lesson covering the required Vyper toolkit, so please review that if you need a refresher or have not gone through the required installation steps.

Vyper for Beginners

Variables

Recall that Vyper is a strongly typed high level language. This means that every variable is declared with a type, and every time that variable is manipulated, the compiler will check to ensure that all components of the statement, expression, or operation are valid for that data type.

As a simple example, consider a generic variable named some_number, which is set to hold only integer values (whole numbers like 1, 2, 3, …, 99, etc.)

If we write a statement that reads some_number = 4, the compiler will first check that the number 4 is an integer, then it will perform all operations necessary to satisfy it.

Whenever a new variable appears in a Vyper contract, it must be declared with a type. This type may not change during the normal course of execution. In other words, a variable declared with type uint256 can and will never hold anything but a uint256 value. Values stored in variables may be converted between types, but may not be re-assigned to the original variable name. In other words, you may convert a uint256 to a decimal value, but must store the converted value in a new variable.

Storage, Memory, Calldata

The Ethereum Virtual Machine (EVM) has three different categories for variables:

Storage variables are recorded directly on the blockchain, are mutable, and are declared outside of any function. They are accessed via the self object, so a storage variable named owner would be accessible via self.owner. Modifying these storage variables is expensive.

Memory variables are declared within a function, are mutable, are not recorded on the blockchain, and are discarded after a function returns. They are only visible within the function itself. Reading and modifying these variables is inexpensive, since they do not consume any blockchain storage space.

Calldata variables are not declared inside the function, are immutable, are not recorded on the blockchain, and are discarded after a function returns. They are used to pass data between functions, and to make the function arguments available inside the function itself.

Types

Vyper provides access to variable types of two categories: Value (any use of the variable in a contract will pass its value directly) and Reference (any use of the variable in a contract will pass a reference to an address in memory, instead of the value stored there).

Value Types

  • Booleans
    • bool (True/False)
  • Numbers — Integers
    • int128 (signed 128-bit integer)
    • int256 (signed 256-bit integer)
    • uint8 (unsigned 8-bit integer)
    • uint256 (unsigned 256-bit integer)
  • Numbers — Decimal
    • decimal (a signed floating point number with 10 decimal point precision)
  • Addresses
    • address (20-byte hexadecimal number, with the familiar 0x leading digit
  • Byte Arrays
    • bytes32 (32-byte array, commonly used to store 256-bit keccak hashes)
    • Byte[x] (a variable-length byte array, where x is the length of the maximum byte array that can be stored)
  • Strings
    • String[x] (a variable-length string byte array that holds alphanumeric strings, where x is the length of the maximum string that be stored)

Reference Types

  • Lists — may hold a finite number of values that share a common value type. Example: uint256[4] is a list that stores four variables of type uint256
  • Dynamic Arrays — similar to lists, except that the array may shrink or grow in size up to the maximum length specified. Example: DynArray[uint256, 4] is a dynamic array that may contain between zero and four uint256 values
  • Structs — a custom type that consists of two or more differing value types grouped together. Example: struct wallet may consist of two internal variables with types address and uint256 value, and could be used to pass a user’s address and balance into a function that expects them to be paired together.
  • Mappings — a hash table that allows certain values to be accessed by an associated ‘key’ value. This is most similar to a dictionary in Python. They are declared in the form HashMap[_keyType _valueType] where _keyType is some value type like uint256, and _valueType is another like address.

NOTE: these variable types are described in great detail in the Vyper documentation, including a list of supported operators, comparisons, and minimum and maximum values.

Default Values

Vyper as no concept of a null (not set) value. It therefore has a default value for all data types, which can be accessed by calling the empty(type) function on the appropriate data type. As an example, empty(uint256) returns 0, empty(address) returns 0x0000000000000000000000000000000000000000 and empty(bool) returns False.

Depending where the variable is first declared within the contract, a variable may or may not require an initial value. For storage variables, no default is required. For memory variables, a default value is required. For calldata variables, the default value is optional.

Public vs. Private

By default, all storage variables are private, which means they are intended to be directly accessed by functions within the smart contract. In contrast, marking a variable public will allow anyone to directly view the value stored within that storage slot.

Side note: Storage on the Ethereum blockchain is accessible even when set to private, and you can still access the data inside a storage slot even if the variable is not marked public. It’s made much easier when you mark a variable as public, though. To make a variable public, wrap the type inside public(). Example: some_var: uint256 becomes some_var: public(uint256).

By declaring a variable as public, Vyper will automatically create a “getter” function of the same name, such as some_var(), that returns the value stored at that location.

Flow Control Statements

Vyper offers only a subset of the flow control options available in Python. Only if and for flow control loops are supported (while is not). The lack of a while loop may require some careful re-factoring of your usual patterns, but a for loop with careful use of break and continue will allow you the same control over typical iterative operations.

Conditional Execution with if/elif/else

The typical conditional execution patterns from Python are available in Python, including the familiar "and", "not", and "in" keywords.

“Truthy” Conversions

A key difference that might trip up Python programmers is that Vyper does not offer any implicit conversion for non-zero values, aka “truthy” comparisons. A typical Python programming pattern (if variable:) will execute the following statement for variable values greater than zero. Vyper does not allow this implicit conversion, but the equivalent is only slightly more verbose (if variable > 0:)

Iteration using for

The for statement is designed to loop through any iterable structure. You may also use the familiar range() construct from Python, which provides similar behavior except for the lack of a custom step value (though there is an open issue on GitHub for this feature).

A “gotcha” that might trip a Python programmer up is that range() only accepts a literal integer greater than zero. In other words, you cannot use the familiar expression for i in range(len(array_name)), since len(array_name) is not a literal. You also cannot do something like…

length = len(array_name)
for i in range(length)

because again, length is not an integer literal!

The solution here is to define a maximum expected number of executions of this loop, and to break it when you reach the end:

length = len(array_name)
for i in range(999999):
    if i == length - 1:
        break
    else:
        [do some other stuff]

This will feel very clunky if you’ve only used Python, but it’s important for EVM since a node must be able to predict gas costs from outside the function before it executes.

Functions

Vyper function declaration is just like Python, with the exception of the requirement to define a return type. Vyper uses the “decorator” formatting scheme to modify the type for all defined functions. The available function types are:

  • @external — the function is accessible from external addresses
  • @internal — the function is inaccessible from external addresses, and only accessible from within this contract
  • @pure — the function does not access any contract state or variables
  • @view — the function does not alter any contract state, but may access the contract state
  • @payable — the function may accept Ether (or the native gas token for this particular blockchain)
  • @nonreentrant(key) — the function may not be called from an external address if a call is currently in progress (first call must complete and return before it may be called again)

All functions must be declared with at least one visibility decorator (@external or @internal), and the rest are optional. These may be combined (unless mutually exclusive), so a function may be have several decorators, such as @external @payable

The functions are defined with this general form:

@decorator1
@decorator2
def function_name(
    argument1: type,
    argument2: type,
) -> type:
    [function logic starts here]

The key difference between a function defined in Vyper vs. Python is the requirement for specifying the return type (syntax -> type before the colon)

the return type may be omitted if the function does not return. This differs slightly from Python, where you would write -> None instead. Vyper does not have a None keyword.

Hello, World!

Every beginning tutorial must include the classic “Hello, World!” example, so here we go! Visit Remix, a browser-based virtual development environment built by the Ethereum team. Using Remix allows you to experiment with smart contracts and simulate their deployments for free inside a fake blockchain that lives inside your browser. It supports both Solidity and Vyper and offers several useful plugins that I encourage you to explore.

Create a new workspace called vyper_hello_world, then in the File Explorers panel, right-click and delete the three example Solidity contracts (1_Storage.sol, 2_Owner.sol, and 3_Ballot.sol). Then right-click the contracts directory and create a new file named hello_world.vy

Then paste the following simple contract into the code window:

message: String[24]

@external
def __init__():
    self.message = "Hello World!"

@external
@view
def hello() -> String[24]:
    return self.message

@external
def change_message(message: String[24]):
    self.message = message

Let’s examine the contract:

The first line defines a private storage variable named message, which stores a String of up to 24 characters.

The first function definition describes an external function named __init__(), also known as the constructor. The constructor is executed automatically, and only once, during the contract deployment. Inside the constructor we store the string "Hello World!" to a variable named self.message. Recall that message is a storage variable defined on the first line. We can access any storage variable using dot notation via the self object.

The second function is an external view function named hello() that returns the value stored at self.message.

Then to finish off, we define a third external function called change_message() that changes the value stored within message.

With this entered, click the 4th button on the Remix toolbar (the V symbol) to access the Vyper compiler. Choose the remote compiler, then click the blue button marked “Compile contracts/hello_world.vy”.

Then click the 3rd button on the Remix toolbar (the Ethereum logo), click Deploy, then wait for a new entry to appear under “Deployed Contracts”. After it does, click the drop-down arrow and you’ll see two buttons: an orange change_message and a blue hello. Clicking the blue hello button will display 0: string: Hello World! which means that the function has returned the value stored at storage slot 0 (a string with contents "Hello World").

Enter a new message in the string message field next to the orange change_greeting button, then click it. You will see a transaction, and then you’ll be able to click the blue hello button again and see the updated message.

Next steps

In the next lesson I will cover interfaces, the necessary gateway between your contract and other deployed contracts on the blockchain. Using interfaces, you will be able to send and receive information and assets between your smart contract and other addresses.

Photo of author

Written By BowTiedDevil

Degenerate coder, open source software maximalist, engineer, turbo autist.

Disclosure

This article may contain links to third-party websites or other content for information purposes. BowTiedIsland may receive a commission at no cost to you if you purchase a product after clicking one of these links. The Third-Party Sites are not under the control of BowTiedIsland, and BowTiedIsland is not responsible for the content of any Third-Party Site. All information contained herein is the opinion of the writer and does not constitute financial advice. We aim to act as a neutral third party and aid in your research and analysis.


The Jungle


Crypto, Investing, and E-Commerce with BowTied Bull

The future is internet based, therefore we have a triangle based approach with crypto, e-commerce business making and Investing in traditional assets

Break Into a 6-Figure Wall Street Career

Learn how to efficiently break into investment banking and private equity. The Banker’s Bible is a definitive resource that offers an   A-Z blueprint and also gives you 24/7 access to real finance professionals. 

Fitness With BowTiedOx

BowTiedOx provides you a place to find all of his latest programs and guides.

Weekly newsletters that cover fitness, health, and mindset, all grounded in the fundamentals of physiology.

Media Production with BowTied Turkey and BowTied Tamarin

Video is no longer optional.

Don’t get left behind.

Your brand deserves professional videos to engage your audience.

Art & Graphic Design with BowTied Patriot

BowTied Patriot is a graphic artist who specializes in photography, mixed medium custom artwork, and NFT creation.

Join BowTiedPatriot as he dives into making Art in Web3.0 and The Metaverse.

Cooking with BowTiedOctopod

Learn secrets from a fine dining chef for maximum flavor and time-saving efficiency

Newsletters on Ingredients, Techniques and Flavor hacks that will have you eating better. We will never eat bugs!

Meme Warfare with DgenFren

Increase your online engagement, organically influence narratives, and build your online persona by using marketing that your target audience actually wants: memes.

Learn How to Sell with BowTiedSalesGuy

Sales is one of the most transferrable life skills, yet few know how to actually sell.

Traditional sales tactics don’t cut it in today’s hyper competitive world.

Learn the secrets from a Chad Salesman and change your Life forever.

Ecommerce with BowTiedOpossum

Learn the skills to start and build your first online business.

Want to build a business that travels with you?

Learn from an industry veteran that has worked on and with brands you already know.