5/5
## Introduction to the Vyper Contract Interaction Challenge This lesson walks through a practical challenge involving interaction with a deployed smart contract written in the Vyper language. We'll use a development environment (similar to Remix IDE) to call functions on the contract, modify its state, and verify the results. The goal is to understand how state changes occur and how data structures like mappings and structs are manipulated within a smart contract. ## Understanding the `FAVORITES` Contract We are interacting with a deployed Vyper contract named `FAVORITES`. Let's examine its key components: **State Variables:** The contract manages several pieces of data stored on the blockchain: * `my_favorite_number: public(uint256)`: Stores a single unsigned integer. * `list_of_numbers: public(mapping(uint256, uint256))`: A mapping that associates an index (key, `uint256`) with a favorite number (value, `uint256`). Think of it like a dynamic dictionary or hash map. * `list_of_people: public(mapping(uint256, Person))`: A mapping that associates an index (key, `uint256`) with a `Person` struct (value). * `index: public(uint256)`: An integer used to track the next available index for adding entries to the `list_of_numbers` and `list_of_people` mappings. **The `Person` Struct:** Vyper allows defining custom data structures. This contract uses a `Person` struct: ```vyper # struct Person: # favorite_number: uint256 # name: String[100] ``` This structure groups a person's favorite number (`uint256`) and their name (`String` with a maximum length of 100 characters). **The `add_person` Function Explained:** The primary function we'll use for this challenge is `add_person`: ```vyper @external def add_person(name: String[100], favorite_number: uint256): # Add favorite number to the numbers list self.list_of_numbers[self.index] = favorite_number # Add the person to the person's list new_person: Person = Person({favorite_number: favorite_number, name: name}) self.list_of_people[self.index] = new_person # Increment the index for the next addition self.index = self.index + 1 ``` Here's how it works: 1. It accepts a `name` and a `favorite_number` as input. 2. It stores the `favorite_number` in the `list_of_numbers` mapping at the *current* value of `self.index`. 3. It creates a new `Person` struct instance using the provided `name` and `favorite_number`. 4. It stores this `Person` struct in the `list_of_people` mapping, also at the *current* value of `self.index`. 5. Crucially, it increments `self.index` by 1, preparing for the *next* call to `add_person`. Initially, we observe that `list_of_people[0]` already contains `(7, "Mark")`, indicating `add_person` was likely called once before our challenge begins, and the `index` variable probably holds the value `1`. ## The Challenge: Populating a Specific Index in a Mapping **The Task:** Can you interact with the deployed `FAVORITES` contract to make querying `list_of_people` at index `3` return the specific data tuple `(8, "Wong")`? Currently, querying `list_of_people[3]` would return default values (likely `(0, "")`), as nothing has been explicitly stored at that index yet. ## Solving the Challenge: Using `add_person` to Control State The key to solving this lies in understanding how the `add_person` function utilizes the `self.index` state variable. Each time `add_person` is successfully executed (as a transaction), it performs two actions relevant to this challenge: 1. It populates the `list_of_people` mapping at the *current* index. 2. It increments the index. If the contract starts with `index = 1` (because index `0` is already filled with "Mark"), we need the `add_person` function to execute when `index` is `3` to store our desired data (`(8, "Wong")`) at `list_of_people[3]`. To achieve this, we need to call `add_person` multiple times: 1. **Call 1 (Hypothetical, already done):** Adds `(7, "Mark")` at `index = 0`. Index becomes `1`. 2. **Call 2:** Input arbitrary data (e.g., "Alice", 5). This adds data at `index = 1`. Index becomes `2`. 3. **Call 3:** Input arbitrary data (e.g., "Bob", 12). This adds data at `index = 2`. Index becomes `3`. 4. **Call 4:** Input the target data (`name: "Wong"`, `favorite_number: 8`). This adds `(8, "Wong")` at `index = 3`. Index becomes `4`. Therefore, the solution involves setting the input fields for the `add_person` function to `name: "Wong"` and `favorite_number: 8` and then executing the function enough times (in this scenario, potentially three more times from the observed state) to ensure it runs when `index` is `3`. In practice, one might repeatedly click the `add_person` transaction button. It's important to execute it the correct number of times. Clicking too many times (as noted in the original demonstration) will still populate index 3 correctly, but the `index` variable will end up higher than necessary, potentially affecting subsequent operations not covered in this specific challenge. ## Verifying the Result After executing the `add_person` function the required number of times with the inputs "Wong" and 8 during the call that targeted index 3, we can verify the outcome. Use the contract interaction interface to query the public `list_of_people` mapping. Input the index `3`. The interface should return the tuple `(uint256: 8, String: "Wong")`, confirming the state was successfully modified as per the challenge requirements. ## Key Concepts Recap This exercise demonstrates several fundamental concepts in smart contract development and interaction: * **State Modification:** Changing the data stored on the blockchain requires sending transactions that execute state-changing functions (like `add_person`). * **Mappings:** Vyper mappings provide a flexible way to store key-value data. They don't have a fixed size but allow accessing values via their keys (indices in this case). * **Structs:** Custom data types (`Person`) allow grouping related information, making the contract code more organized and readable. * **Function Interaction:** Using a development environment or library to call functions (`add_person`) on a deployed contract. * **Index Management:** Using a dedicated state variable (`index`) to control the insertion position within mappings, simulating array-like sequential addition. * **Gas Costs:** Remember that every state-changing transaction (like each call to `add_person`) consumes gas on a live blockchain network.
This lesson walks through a practical challenge involving interaction with a deployed smart contract written in the Vyper language. We'll use a development environment (similar to Remix IDE) to call functions on the contract, modify its state, and verify the results. The goal is to understand how state changes occur and how data structures like mappings and structs are manipulated within a smart contract.
FAVORITES
ContractWe are interacting with a deployed Vyper contract named FAVORITES
. Let's examine its key components:
State Variables:
The contract manages several pieces of data stored on the blockchain:
my_favorite_number: public(uint256)
: Stores a single unsigned integer.
list_of_numbers: public(mapping(uint256, uint256))
: A mapping that associates an index (key, uint256
) with a favorite number (value, uint256
). Think of it like a dynamic dictionary or hash map.
list_of_people: public(mapping(uint256, Person))
: A mapping that associates an index (key, uint256
) with a Person
struct (value).
index: public(uint256)
: An integer used to track the next available index for adding entries to the list_of_numbers
and list_of_people
mappings.
The Person
Struct:
Vyper allows defining custom data structures. This contract uses a Person
struct:
This structure groups a person's favorite number (uint256
) and their name (String
with a maximum length of 100 characters).
The add_person
Function Explained:
The primary function we'll use for this challenge is add_person
:
Here's how it works:
It accepts a name
and a favorite_number
as input.
It stores the favorite_number
in the list_of_numbers
mapping at the current value of self.index
.
It creates a new Person
struct instance using the provided name
and favorite_number
.
It stores this Person
struct in the list_of_people
mapping, also at the current value of self.index
.
Crucially, it increments self.index
by 1, preparing for the next call to add_person
.
Initially, we observe that list_of_people[0]
already contains (7, "Mark")
, indicating add_person
was likely called once before our challenge begins, and the index
variable probably holds the value 1
.
The Task: Can you interact with the deployed FAVORITES
contract to make querying list_of_people
at index 3
return the specific data tuple (8, "Wong")
?
Currently, querying list_of_people[3]
would return default values (likely (0, "")
), as nothing has been explicitly stored at that index yet.
add_person
to Control StateThe key to solving this lies in understanding how the add_person
function utilizes the self.index
state variable. Each time add_person
is successfully executed (as a transaction), it performs two actions relevant to this challenge:
It populates the list_of_people
mapping at the current index.
It increments the index.
If the contract starts with index = 1
(because index 0
is already filled with "Mark"), we need the add_person
function to execute when index
is 3
to store our desired data ((8, "Wong")
) at list_of_people[3]
.
To achieve this, we need to call add_person
multiple times:
Call 1 (Hypothetical, already done): Adds (7, "Mark")
at index = 0
. Index becomes 1
.
Call 2: Input arbitrary data (e.g., "Alice", 5). This adds data at index = 1
. Index becomes 2
.
Call 3: Input arbitrary data (e.g., "Bob", 12). This adds data at index = 2
. Index becomes 3
.
Call 4: Input the target data (name: "Wong"
, favorite_number: 8
). This adds (8, "Wong")
at index = 3
. Index becomes 4
.
Therefore, the solution involves setting the input fields for the add_person
function to name: "Wong"
and favorite_number: 8
and then executing the function enough times (in this scenario, potentially three more times from the observed state) to ensure it runs when index
is 3
.
In practice, one might repeatedly click the add_person
transaction button. It's important to execute it the correct number of times. Clicking too many times (as noted in the original demonstration) will still populate index 3 correctly, but the index
variable will end up higher than necessary, potentially affecting subsequent operations not covered in this specific challenge.
After executing the add_person
function the required number of times with the inputs "Wong" and 8 during the call that targeted index 3, we can verify the outcome.
Use the contract interaction interface to query the public list_of_people
mapping. Input the index 3
. The interface should return the tuple (uint256: 8, String: "Wong")
, confirming the state was successfully modified as per the challenge requirements.
This exercise demonstrates several fundamental concepts in smart contract development and interaction:
State Modification: Changing the data stored on the blockchain requires sending transactions that execute state-changing functions (like add_person
).
Mappings: Vyper mappings provide a flexible way to store key-value data. They don't have a fixed size but allow accessing values via their keys (indices in this case).
Structs: Custom data types (Person
) allow grouping related information, making the contract code more organized and readable.
Function Interaction: Using a development environment or library to call functions (add_person
) on a deployed contract.
Index Management: Using a dedicated state variable (index
) to control the insertion position within mappings, simulating array-like sequential addition.
Gas Costs: Remember that every state-changing transaction (like each call to add_person
) consumes gas on a live blockchain network.
A hands-on exercise for the Vyper Contract Interaction Challenge - Discover how repeated function calls modify contract state, focusing on populating specific indices in Vyper mappings. Learn the mechanics of interacting with deployed Vyper contracts and manipulating structs.
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