- Lab
- Core Tech

Guided: Exploring Advanced Solidity Concepts - Part 1
In this hands-on lab, you'll build upon the knowledge gained from the Solidity introduction lab. You'll dive deeper into essential topics such as contract inheritance, interfaces, function modifiers, error handling, event logging, abstract contracts, and libraries. Through engaging hands-on exercises, you'll further enhance your skills in designing and developing cutting-edge decentralized applications. This lab represents the next level in creating robust and secure smart contracts, empowering you to take your blockchain development journey to new heights.

Path Info
Table of Contents
-
Challenge
Introduction
Welcome to the "Guided: Exploring Advanced Solidity Concepts" lab!
Expanding upon the concepts established in the "Guided: Introduction to Solidity Development using Hardhat" lab, this two-part exploration will take you further into the intricate world of Solidity by introducing advanced features.
Using the
Tickets.sol
contract you'll find to the right, you'll discover how to implement Libraries, Events, Function Modifiers, Inheritance, and even the process of importing external files.This contract centers around managing tickets by mapping account addresses to their claim statuses. Through the constructor, it is deployed with a total number of available tickets.
The heart of its logic lies in the
claimTicket()
function.This function first requires that the caller has not already claimed a ticket. If they have not, then add them to the
ticketsClaimed
mapping and decrement the number of available tickets. Here is one of the first areas that needs to be addressed. Considering thatavailableTickets
is an unsigned integer, there is a potential underflow exception that could occur if there are0
available tickets. You'll learn a couple ways of addressing that condition and how to share that logic across contracts.Considering potential improvements to this function, it could be helpful to inform others about this change using Solidity Events. You'll explore the benefits of emitting events from your contracts.
In addition to this core functionality, there are two additional functions to help support this core feature.
The
ticketClaimed()
function is used to verify if the caller has already claimed a ticket. While helpful specifically for the caller, you may also want to be able to verify if a different account has claimed a ticket.The
addTickets()
function enables the caller to increase the number of available tickets. For security purposes, it's sensible to restrict this function to the contract owner. In fact, therequire
condition that enforces this restriction could be useful in other parts of the contract as well. You'll investigate ways to share this logic across different functions within the same contract, and potentially across other contract files.When you are ready to begin exploring these concepts, click the Next step > button below.
-
Challenge
Sharing Functionality with a Library
The first thing that needs to be addressed is the potential underflow exception when trying to subtract
1
when theavailableTickets
is already0
. SinceavailableTickets
is an unsigned integer type, trying to set its value to-1
would cause an underflow exception. When exceptions occur in a solidity contract all state changes are reverted, all remaining gas in the transaction is consumed, and the exception propagates up the chain of calls. For this simple contract, that is not a concern, but when your contracts grow in complexity these will definitely become more of a concern.To demonstrate this error open the
test/StepTwoTests.js
unit test file and specifically look at the unit test "Unsigned Underflow Error". Quickly reviewing this test:- The
Tickets
contract is being deployed with only1
ticket. Note that it is being deployed under the first address returned by theethers.getSigners()
method, stored in theowner
variable. - The next address, stored in the first position of the
addr
array, claims that ticket. - The next address,
addr[1]
, will attempt to also claim a ticket, but it should not be able to do so since there is only one available ticket. There are a couple of ways this panic code can be mitigated. The first would be to add arequire
statement to the top of the function. This would mitigate this specific instance of this exception, though the potential exception still lurks within the logic of the function.
Another option would be to create a safer math library that can be used to safely perform operations that could head off these potential exceptions. Rerunning the unit test command, you will see that there is still an
Exception
thrown, but with the message of therequire
condition provided in thesub()
function. Remember that functions that have been reverted by arequire
condition will refund any remaining gas not yet consumed. As compared to a panic exception that will not refund the remaining gas. Rerunning the unit test command, you should now have a passing unit test.When you are ready, click the Next step > to continue.
- The
-
Challenge
Event Logging and Querying in Smart Contracts
One of the key benefits of a smart contract is its ability to store information on an immutable ledger. Although the
Tickets
contract is relatively simple, it demonstrates the fundamental functionality of maintaining state across various accounts. Once a caller claims their ticket, that claim will be permanently recorded on the EVM, unless the contract explicitly provides functionality to reverse it.As the contract currently stands, only the individual caller can modify and review their particular state, specifically to claim and check their ticket claim. However, when building a web3 decentralized application (DApp), it's often essential for the application to be aware of certain events, such as when a ticket is claimed. This is where the Ethereum network's ability to store events in a public log comes into play, as it allows for subscription or querying of these logs.
Your DApp can either subscribe to this log to receive real-time notifications of specific events, or it can query the logs to search for particular historical events. In fact, some DApps utilize these logs to perform various functions, such as maintaining information off-chain to avoid incurring gas costs, or to delegate some computational work to external systems. This might include subscribing to the event log and triggering external processes, like a fulfillment process, thus creating a bridge between on-chain and off-chain functionalities. To observe this firsthand, you will once again leverage HardHat's unit testing capabilities. However, similar results can be achieved using any JavaScript client that employs a web3 library, like web3.js. For this demonstration, we're using HardHat's ethers.js variant. While there might be minor modifications required for direct implementation in a real-world web3 DApp, the core concepts remain consistent.
The unit test file
test/StepThreeTests.js
contains another faux unit test to help demonstrate reading these events from the logs. You have seen the first part of theQuery from Contract
unit test before where the contract is deployed and a ticket is claimed. The logs are then accessed through the contract to filter for the specificTicketClaimed
event. Querying the logs through the contract has hidden some of the complexities of querying logs, such as identifying the network provider, which Hardhat has encapsulated in the contract'sdeploy()
function.Additionally, the event's signature,
TicketClaimed(address)
, has been encoded and stored in thetopics
array of the log. In fact, almost all aspects of the log are stored as hashes of the actual value. The human-readable elements ofinterface
,fragment
, andargs
in the returnedEventLog
have been conveniently added by thequeryFilter()
method.As you proceed in your Solidity learning, you can explore these complexities through the
provider.getLogs()
method.When you are ready, click the Next step > to continue.
-
Challenge
Enhancing Flexibility with Function Overloading
As mentioned in the introduction, the
ticketClaimed()
function is particularly helpful for the caller, as it checks whether the caller has claimed a ticket. An alternate version of this function, taking an address as a parameter to verify whether that address has claimed a ticket, would also be beneficial. Providing multiple functions with different parameters, referred to as function overloading, enables more flexible usage of the function within the contract.For this step, you'll take a different approach. Opening the
test/StepFourTests.js
file, you'll see unit tests for both overloaded versions of theticketClaimed()
function. Unsurprisingly, the second unit test is failing, since the overloadticketClaimed(address _account)
function has not been created, although the reason it is failing might surprise you.The first
expect
assertion is trying to call the overload functionticketClaimed(address _account)
, which again has not yet been created, using theowner
account. It passes inaddr[0]
to check if a ticket has been claimed. Currently this is returning false, but once the overloaded function is created, this should return true.The second assertion calls the function with the
addr[0]
account, checking if the owner has claimed a ticket. This should return false, as theowner
account has not claimed a ticket. However, if you comment out the first assertion and rerun the test, you'll find that this call returns true. It seems that the call is ignoring the parameter and invoking the parameterlessticketClaimed()
function, essentially checking if the calleraddr[0]
has claimed a ticket.When working with overloaded functions in smart contracts, it's crucial to ensure that the functions are called with the exact expected number of parameters. A mismatch in parameters can lead to unexpected behavior and serious issues in your contract's functionality.
You can uncomment the first
expect
assertion and proceed with creating the overloaded version ofticketClaimed()
that takes a parameter. Rerunning the unit test file:npx hardhat test test/StepFourTests.js
You are now presented with the error message:
TypeError: ambiguous function description
(You may need to scroll up to see this.)
It appears that ethers.js does not directly support the ambiguity of overloaded functions. Although it does provide a means to explicitly call the overloaded function. Rerunning the unit test file should now succeed:
npx hardhat test test/StepFourTests.js
Solidity does support function overloading, allowing multiple functions with the same name but different parameters. It appears that ethers.js javascript framework requires an additional step in order to be able to call them. Due to this complexity, you may want to carefully consider whether this additional effort makes sense for your specific implementation. Weighing the benefits against potential confusion or errors will help you make an informed decision.
When you are ready, click the Next step > to continue.
-
Challenge
Next Steps
Congratulations on completing the first part of this Exploring Advanced Solidity Concepts Lab.
In Part 2 of this lab you will explore how to export common functionality so that it can be shared across functions, contracts, and even across different files.
Hope to see you soon in the next lab, "Guided: Exploring Advanced Solidity Concepts - Part 2".
Here's to your continued success in Blockchain training and Solidity contract development! We look forward to seeing what you will create.
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.