No more scalping…
Creating eventChain: a web3.0 ticket sales dApp that gives tickets intrinsic value by creating them as NFTs
When was the last time you printed out tickets to enter an event?
Judging by the age demographic of readers on Medium, I am going to assume this is not your preferred way of carrying tickets. Thanks to technology like Apple/Google Pay, gaining access to events involves nothing more than double-clicking your phone’s power button.
But this advent of QR code ticketing has its many issues:
- Trading bots can purchase all the tickets for a certain event and sell them on secondary sites to make an unfair profit. This issue was apparent just this year with the scalping bot scandal for Taylor Swift tour tickets. This problem was so serious it had its own senate judiciary hearing in January!!
- Because original ticket vendors have no way to record who secondary ticket sellers sell to, they actually have no way to access the information of who is attending their event. There is no way for event organizers to know how many times a ticket changed hands before their event
- Customers don’t know what tickets are real vs fake. Because there is no visible paper trail to validate the ticket they are buying, oftentimes malicious secondary vendors sell fake QR codes to people who don’t know they are fake until they arrive at the event.
So how can we address these problems?
At its peak, the Non-Fungible Token trading market reached an aggregate value of just over $25 billion. But most of this value was from inflated abstract art from projects like the Bored Ape Yacht Club and CryptoPunks. As a result, many of the other more practical applications of NFTs were overlooked.
Fundamentally, NFTs are a means to authenticate and prove ownership of digital assets. And tickets right now are digital assets that lack a ubiquitous method of digital ownership. Because of their unique relation to smart contracts and an immutable public ledger, using NFTs as tickets ensures secure ownership, prevents counterfeit tickets, and restricts trading bots’ ability to “beat” the market. On top of these reasons, ticket transactions will be transparent so ticket vendors still have data about their customers beyond their primary economy.
But most importantly, the use of NFTs cuts out the need for a middleman in the ticket sales industry. Tickets can be sold or transferred using a smart contract as a middleman to collect and transfer funds as well as mint the desired number of NFTs to be sold. This facet removes the absurd fees that often go along with ticket sales on sites like TicketMaster and Seatgeek. This feature also empowers ticket holders with greater control over their tickets and fosters a safe, transparent secondary market for ticket resale.
These smart contracts are self-executing agreements with predefined functions that run in different situations. By leveraging these “middlemen,” the entire ticket sales process is automated in a way that ensures tickets are sold at fair prices because its predefined rules dictate their prices according to market demand.
On top of this, smart contracts can impose resale royalties to respect the efforts of artists, performers, athletes, etc to pay homage to the efforts of these esteemed individuals. This feature creates an economy where the more hands that tickets change, the better it is for the artist, performer, or athlete.
Beyond the core functionality, NFT based tickets can also open avenues for personalized engagement through avenues such as exclusive perks, backstage passes, access to limited-edition merchandise, etc. In this way, these digital collectibles live far beyond the single events they represent and create long-lasting communities.
Overcoming Challenges and Adoption Hurdles
Despite the potential benefits, the adoption of NFT-based event tickets faces certain challenges. Primarily, there is a learning curve for both organizers and attendees to understand the intricacies of NFTs and blockchain technology. Additionally, scalability concerns plague the industry due to the high volume of transactions that would take place if adoption becomes near-ubiquitous. Moreover, interoperability between different blockchain platforms must be achieved to ensure seamless ticket transfers and compatibility across various ticketing systems.
Enough theorizing, let’s get into the app
Introducing… eventChain
EventChain is a decentralized ticket sales platform creating an economy between ticket vendors and buyers.
Check out this full stack github repo of the code
This app has many components in the backend and frontend that interact to create the functionality of this decentralized economy.
Backend:
- The @openzeppelin library for allowing us to use the ERC-721 behavior to create tokens
- The EventChain.sol smart contract with the core functionality
- Deploy.js file that allows the smart contract to interact with the blockchain network
- EventChain.js file for testing our smart contract functions before deploying them
Frontend:
- All the react files for each of the components in the interface
- CSS styling files for each component of the interface
RoadMap
Now, let me explain how all the components of the app interact.
The developer (owner) sets the number of tickets available and the cost of each ticket (in eth) in the deploy.js file to interact with the blockchain network. In order to mint the NFTs (tickets), there is a mint function in the smart contract which creates the number of NFTs specified by the developer.
From then, the end user interacts with the front end through their meta mask wallet. When they click on a seat to buy it, they are paying the smart contract in exchange for the minted NFT ticket. This money is then held by the smart contract until the developer withdraws the money from the smart contract leaving its final balance empty.
Let’s break down the code in the smart contract
By importing the open Zeppelin library, we are gaining access to all the standard ERC-721 functionality for free. By inheriting from the ERC-721 smart contract, our smart contract can access the token’s pre-defined functions and override those functions with its custom code. This ability is super important for how the NFTs are distributed in the smart contract to the end user.
The keyword “is” means that the eventChain class is a subclass of ERC-721 and therefore inherits all of its functions. This feature is a key component of object-oriented programming languages like Solidity.
Solidity allows us to create custom data types using the keyword “struct.” In this case, we passed in all the details of the different occasions (we can’t use the word events as that is a Soldity keyword) as parameters for the struct to contain. These variables are used in functionality later and are set by the owner/developer in the deploy.js file to set parameters for the details of the event they are planning.
After defining the occasion variable, we use key-value pairs via the mapping data type to create some relation between specific elements. This mapping datatype acts as a hash table to store state variables. For the occasions map, we relate an id as an unsigned integer to the different occasions that will be inputted.
For seatTaken, we nest mapping statements to relate seat number to who took the seat to the occasion number. For seatsTaken, we simply track the number of seatsTaken by iterating through the array.
For hasBought, we first check if the specific addresses of users have bought a ticket already. Then relate that to a seat number to make sure the same person can’t buy the seat twice.
Modifier functions allow us to change the behavior of pre-defined functions. In this case, we use “require” to make sure that the person operating is the owner/developer as they would be the only one allowed to withdraw money from the contract. When we apply onlyOwner to any random function, the function will only execute if this require statement is true and the person calling that function (withdrawing money from the smart contract) is the owner/developer. We have to include the underscore as a separate line to indicate that the require statement would run before all the rest of the code.
The next constructor inherits from the ERC721 class and initializes the main instance variables used for the owner of the event. This logic implies that the only person calling the constructor would be the owner, which makes sense as there is no way for the end user to call the constructor via the interface.
list is the main function used to store the events and their details. We make this function onlyOwner() so only the event owner/developer can make changes to the event details. We also increment the number of occasions every time an occasion is created indicating there is another occasion for a user.
We then use the struct variable defined above and pass in the new parameters specified by the developer to save them to the blockchain. These parameters are specified in the deploy.js file as that is what communicates with the smart contract and the blockchain network.
mint is a function that actually creates the NFTs for the event tickets. The keyword “payable” is a modifier for the smart contract that allows users to pay the smart contract via their meta mask wallet for ownership of the NFTs. This money is then held in the smart contract until withdrawn by the owner/developer. This mint function is similar to a function in the open zeppelin library so we add our unique code and then chain the _safeMint method as the last statement.
Within this method, we make sure the id for the occasion is actually valid, check that they have enough money to buy the ticket, and check that the seat is valid before minting the NFT. If all the checks pass, then the total number of tickets available decrements, ownership is transferred to the user, and the total supply of seats is updated accordingly.
The first two methods are just public accessor methods to provide access to the private instance variable of the number of seats taken and the Occasion details for a specific id.
The final function allows the owner to withdraw the funds from the smart contract. Once again, we modify the function to only provide access to the owner as only the owner should be able to receive the funds for the tickets they sold.
This function uses a unique pair of nested calls to send the owner the metadata of the crypto. This call usually comes with a message but there is no message needed here so the string is empty. The “value:” syntax indicates that metadata is being sent. We then ensure that this transaction went smoothly by requiring success. This is an important statement because if the function was not successful and continued to run, the owner’s money would be stuck in the smart contract.
And that’s all for the smart contract
Importance of a deploy file
Smart contracts handle the solidity code to run certain functions based on actions that occur on the blockchain. But to actually deploy that smart contract onto the Ethereum network and communicate with the different blockchain platforms, there needs to be some Javascript code to handle these actions.
This deployment file allows you to set specific arguments for the contract such as its gas limits and network endpoints. You also have your deploy file to specify which network you want the contract to be deployed on (mainnet or testnets). With this case, you are able to deploy it to multiple networks for testing efforts.
In the initial portion of this deploy file, we specific we want the contract deployed on the hardhat test network (my favorite test network due to its freedom and pre-defined functionality).
The next function takes in the numerical amount n in ether and returns the number of tokens.
We then enter the main function which we set as asynchronous so it can run without having to wait on anything else. On the first line of the function, we get the Ethereum account of the user by using the getSigner. This account is stored as the deployer value.
We then create the EventChain and eventChain variables to actually deploy the contract. First, we store the contract factory for the event chain contract. We use this factory to help deploy the contract in the next line. By calling the .deploy function in the next line, we are creating a new object and passion in the Name and Symbol as the constructor arguments.
We wait for the contract to be deployed before continuing with the code with the “await” as that is the core functionality of this deploy file.
This array is one of the events we as the developer made. Each data field corresponds to the ones we defined in the smart contract and when the contract is deployed, these fields are passed in as the data for the first element in Occasions.
As the final part of the deploy file, we loop through each of the data fields for each respective Occasion and essentially add the data to the contract.
We connect to the contract using the developer’s account who signs it with his private key. This data is then passed through the list function in the smart contract.
The importance of testing our smart contract functions
The blockchain is an immutable ledger so once a contract is deployed, it will forever stay on the blockchain. As a result, it is important we run tests locally before deploying any important functionality to avoid having to keep deploying dysfunctional code.
Here is an example to test that the occasion-adding function worked. We use features like “describe” and “it” to test the functions in the smart contract actually work. Describe is used to set up a group of test cases that are to run and it is used to set the specific test case to ensure the total number of occasions changes by one every time an occasion is added
The next set of “it” tests ensure the specific occasion parameters are valid from the smart contract. We use “expect” statements to make these checks as we expect the smart contract functions to work.
Here is another example of the test case for the owner withdraw function. Again we are using keywords like “describe”, “it”, and “expect” to test the solidity functionality.
However, unique to this function, we use a “beforeEach” function to get the amount of money in the smart contract before the withdrawal to be compared to the amount after (should be 0). In this function, we mint the NFT, then withdraw the money from the user’s wallet (paying for the ticket).
We then use “it” statements to check that the owner’s balance is correct after the withdraw functions runs and then check that there is no money left in the smart contract. With all this functionality, the withdraw function works.
Dor testing our code in the TokenMaster.js file, we would always use “npx hardhat test” in the command line and then the strings in the “it” and “describe” parameters are displayed along with whether or not the test cases pass.
Connecting our front end and backend
We can write the code for the smart contract and deploy it and then we can write the react script for the front end, but how do the two components communicate?
Through the use of an Application Binary Interface (abi), the front end can interact with the smart contract deployed on chain. This abi is essentially a JSON representation of the smart contract and its functionality to be integrated into the front end code. This file is crucial for creating the full stack app to ensure a smooth user experience.
Front end
I am not going to delineate all the code in the front end as it is a lot of HTML and CSS to layout pieces of the interface, but it is important I explain these initial important statements to gain a key understanding of the full stack connectivity of the app.
We first import the “hooks” from the React.js package to be used when writing the frontend. This import allows us to seamlessly integrate the frontend features.
We then import the ethers.JS library so we can use the API and other tools that allow us to connect the web3.0 functionality to the javascript file.
The next important import to note is the ABI we import to have the smart contract functionality integrated into the front end.
And finally, we import the JSON configuration file as that contains the address of the owner to which the user sends the eth to to receive their tickets. This import provides the necessary data for complete transactions.
And that’s all…
Leveraging NFTs as event tickets holds great promise for the future of the ticketing industry. The unique properties of these tokens coupled with the automation and trust of smart contracts offer countless benefits for both event organizers and attendees. We hope this decentralized economy will help enhance transparency, eliminate fraud, enable secure transfers, and bring together communities of fans.
However, it is important we address the issues of scalability, education, and interoperability before expecting widespread adoption. As we continue to explore the potential of NFTs and smart contracts, we can anticipate a future where attending events becomes more secure, efficient, and immersive for all!
Thanks for checking this out! To stay updated along my building and writing journey, drop a follow for my medium account. Also, check out my monthly newsletters to follow my monthly growth and projects as well as insights in Web3.0, climate tech, and more! Feel free to reach out to me on Linkedin and check out my full portfolio for other projects and experiences.