5/5
## Recap: Understanding Your First Vyper Smart Contract - `favorites.vy` Congratulations on writing your first Vyper smart contract! This lesson recaps the fundamental concepts we covered while building the `favorites.vy` contract using the Remix Ethereum IDE. Understanding these basics is crucial before moving on to more complex topics. Let's revisit the key elements of our contract: ### Vyper Version Pragma The very first line in our contract specifies the compiler version: ```vyper @version 0.4.0 ``` This `@version` pragma is essential. It tells the Vyper compiler exactly which version the contract was written for. This prevents compatibility issues that might arise with future compiler updates, ensuring your code behaves as expected. ### Structs: Defining Custom Data Types We needed a way to group related information, specifically a person's name and their favorite number. Vyper's `struct` allows us to create custom data types for this purpose: ```vyper struct Person: favorite_number: uint256 name: String[100] ``` Here, we defined a `Person` struct containing an unsigned integer (`uint256`) for the `favorite_number` and a fixed-size string (`String[100]`) for the `name`. Structs help organize data logically within your contract. ### State Variables: Storing Data on the Blockchain Variables declared outside of any function are called state variables. They represent the data stored persistently on the blockchain by our contract. Any changes made to these variables are recorded permanently (and typically cost gas). In `favorites.vy`, we declared several state variables: ```vyper # Simple types my_name: public(String[100]) my_favorite_number: public(uint256) # Initialized to 7 later # Fixed-size arrays (lists) list_of_numbers: public(uint256[5]) # Defaults to [0, 0, 0, 0, 0] list_of_people: public(Person[5]) # Array of our custom struct # Index for managing arrays index: public(uint256) # Defaults to 0 # Mapping (key-value store) name_to_favorite_number: public(HashMap[String[100], uint256]) ``` Notice the different data types used: * `uint256`: For whole non-negative numbers. * `String[100]`: For text, limited to 100 characters. Fixed sizes are common in Vyper for predictability. * `uint256[5]`: A list that can hold exactly 5 unsigned integers. * `Person[5]`: A list that can hold exactly 5 `Person` structs. * `HashMap[String[100], uint256]`: A mapping (like a dictionary or hash table) storing key-value pairs, where keys are strings and values are numbers. ### Visibility: Controlling Access with `public` We declared all our state variables using `public(...)`. This visibility modifier does two things: 1. It makes the variable's value readable from outside the contract. 2. It automatically creates a "getter" function. In Remix, these appear as blue buttons, allowing anyone to easily query the current value of the variable without needing a custom function like `retrieve`. ### The Constructor: Initializing Your Contract When a contract is deployed to the blockchain, we often need to set up its initial state. This is done using the constructor function, marked with the `@deploy` decorator and named `__init__`: ```vyper @deploy def __init__(): self.my_favorite_number = 7 self.index = 0 self.my_name = "Patrick!" # Feel free to change this to your name! ``` This `__init__` function runs only *once* during deployment. Inside it, we use the `self` keyword to access and assign initial values to our state variables (`my_favorite_number`, `index`, `my_name`). ### Functions: Defining Contract Behavior Functions define what the contract can *do*. They contain the logic for interacting with and modifying the contract's state. Functions are defined using the `def` keyword. **External Functions (`@external`)**: These functions can be called by external accounts (users) or other contracts. If you omit a visibility decorator like `@external`, the function defaults to `@internal`, meaning it can only be called from within the same contract. **View Functions (`@view`)**: These functions promise *not* to modify the contract's state. They only read data. Because they don't change anything on the blockchain, calling them doesn't require sending a transaction or spending gas for execution (though network provider fees might still apply). Let's look at our examples: 1. **`store` (State Modifying Function):** ```vyper @external def store(new_number: uint256): self.my_favorite_number = new_number ``` This function is marked `@external` so it can be called from outside. It takes one input (`new_number`) and uses `self` to update the `my_favorite_number` state variable. Since it modifies state, calling it requires a transaction. 2. **`retrieve` (View Function):** ```vyper @external @view def retrieve() -> uint256: return self.my_favorite_number ``` Marked `@external` and `@view`. It simply reads and returns the current value of `self.my_favorite_number`. It doesn't change state, so it's a view function. 3. **`add_person` (Complex Function):** ```vyper @external def add_person(name: String[100], favorite_number: uint256): # Store the number in the numbers list using the current index self.list_of_numbers[self.index] = favorite_number # Create a new Person struct in memory new_person: Person = Person(favorite_number=favorite_number, name=name) # Store the new person in the people list at the current index self.list_of_people[self.index] = new_person # Map the name to the favorite number self.name_to_favorite_number[name] = favorite_number # Increment the index for the next entry self.index = self.index + 1 ``` This function combines several concepts. It's `@external`, takes multiple inputs, creates a `Person` struct instance, modifies multiple state variables (`list_of_numbers`, `list_of_people`, `name_to_favorite_number`, and `index`) using `self`, and uses array indexing. It clearly modifies state and requires a transaction. ### The `self` Keyword Crucially, inside any function (`__init__`, `store`, `add_person`, etc.), you *must* use the `self` keyword to access or modify the contract's state variables (e.g., `self.index`, `self.my_favorite_number`). This distinguishes persistent state variables from temporary function parameters or local variables (like `new_number` in `store` or `new_person` in `add_person`). ### Remix Ethereum IDE: Your Development Environment Throughout this process, we used the Remix Ethereum IDE. It's a powerful, browser-based tool perfect for learning, writing, compiling, deploying, and interacting with smart contracts. Its interactive interface, like the blue buttons for public variables and external functions, makes understanding contract interaction much easier. Even professional security researchers use Remix to quickly test ideas. ### Key Takeaways * **`@version`**: Locks the compiler version. * **`struct`**: Creates custom data types. * **State Variables**: Store persistent data on-chain. Declared outside functions. * **`public`**: Makes state variables readable externally and creates getter functions. * **Data Types**: Define the kind of data (e.g., `uint256`, `String`, arrays, `HashMap`). Vyper often uses fixed sizes. * **`@deploy def __init__`**: The constructor, runs once on deployment to initialize state. * **`def`**: Defines functions. * **`@external`**: Makes functions callable from outside. * **`@view`**: Marks functions that only read state (no gas cost for execution). * **`self`**: Used inside functions to access state variables. * **State Modification**: Requires transactions and costs gas (e.g., `store`, `add_person`). * **State Reading**: Can be done via `@view` functions or public getters, generally without execution gas costs. You've successfully navigated the core components of a Vyper smart contract! You've learned about versioning, data structures, state management, functions, visibility, and the development environment. **Important Advice:** You've absorbed a lot of information. Now is the perfect time to take a break. Step away, grab a coffee or some ice cream, go for a walk, or hit the gym. Letting your brain rest and process this information is crucial for effective learning. Well done on completing your first contract!
A foundational review to Understanding Your First Vyper Smart Contract - `favorites.vy` - Revisit core Vyper elements like version pragmas, structs, state variables (types, arrays, maps), visibility, and the `__init__` constructor. Understand external vs. view functions and the use of `self` for interacting with contract state.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
The basics of blockchain transactions, how to send and receive money on a blockchain network.
How to write Python based smart contracts using Vyper.
How to read and understand Vyper smart contracts.
Vyper data structures, arrays, structs, hash maps.
How to build a smart contract application and deploy on ZKsync with Moccasin.
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
On-chain Data Analyst
$59,000 - $139,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 11, 2025
Duration: 2h 08min
Duration: 2h 32min
Duration: 24min
Course Overview
About the course
The basics of blockchain transactions, how to send and receive money on a blockchain network.
How to write Python based smart contracts using Vyper.
How to read and understand Vyper smart contracts.
Vyper data structures, arrays, structs, hash maps.
How to build a smart contract application and deploy on ZKsync with Moccasin.
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
On-chain Data Analyst
$59,000 - $139,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 11, 2025