Transcription
Hey everyone, and welcome back to another tutorial video. In today's tutorial video, we're going to be building our very own simple prediction market web3 application. With the rise of prediction markets, especially that of Poly Market, which you may have heard with the 2024 US election, we're going to be taking a look at prediction markets and how we can build our very own simple version of one.
We'll be building our very own prediction market smart contract along with our very own web3 application using Thirdweb. So, an overview of what we're going to be covering in this video: we'll first go through a demo of the prediction market application we're going to be building. That way, you can get a sense of what we're going to be learning in this tutorial video. Then, we'll take a quick overview and look at what prediction markets are, how they work, why they're so important, and why they really became so popular.
Then, we'll dive in and create our very own simple prediction market smart contract. We'll deploy it ourselves, and then we'll build our very own web3 application to interact with the smart contract we just built. So, all of that being said, let's jump on the computer. Here, let's take a quick look at the demo before we dive into what our prediction market is.
And here is our simple prediction market. Again, this is a simplified version of a prediction market, similar to something like Poly Market. Over here, you can see our title. Here, we have a sign-in button, which will connect a user's wallet to our application here. We'll go over that in just a moment. And then down here, we have our different tabs for active markets, we have pending markets that need a resolution still, and then we have our resolved markets where users can then claim their rewards if they have any for those markets.
Down here are our different markets. In this example here, I have like NFL games for this weekend where we have the Raiders and Dolphins, and then users can select which option or choice they want to choose, and then they can buy shares there. Now, I'm going to hit the sign-in button here, and we're going to be using in-app wallets or embedded wallets in combination with account abstraction and smart wallets. So, a user is going to be able to log in with a familiar auth method like a Google email or passkey, and then we're going to generate them a smart contract wallet. And because we're using account abstraction, we can use features like sponsor gas, and a user doesn't need to fund their wallet or anything, and we can then sponsor the gas on their behalf, and a user can just jump into using our application here.
So, I'm going to use Passkey as an example here. I'm just going to sign in here. I'm going to connect with a smart account here. And once I'm logged in, you're going to see I have this claim token button. Now, this is just for the demo purpose, and this claim token, when I click on it, just claims my account here 100 predict tokens, which is a token that is used within our prediction market here. So, we have a custom ERC20 token. You can see my tokens have just been claimed that we can use to now purchase the shares that we want for the different markets, and we can start using it.
Now, ideally, you probably don't want to just have a button where, you know, users can claim these tokens for free, but you can, you know, put this behind a like gated paywall, you can put this behind like behind having to maybe complete certain quests or activities in order to claim these tokens. You can get creative with how you want to distribute that. We are using Thirdweb's Engine, which we're going to go over in a tutorial on how you can implement that into your application as well, but just know that just for demo purposes, we have a button that you can just claim as much tokens as you want, just so that we can actually use our application here.
But down here, you can see we have different markets. So, over here, we have Raiders Dolphins, and right now, I currently have 12 shares with this account on Raiders. If I wanted to, I can get a little bit more here. So, let's just say I want to do purchase three more to get this to 15. I can select that, I hit confirm. Now, we're going to have to set approval because we are using a custom ERC20 token here. Our user does need to approve the spending and transaction of that token here with this contract. Of course, because we are using something like account abstraction, you could actually batch these transactions together and just approve and confirm in one go, but I wanted to split the steps here just so I could walk through the process of which this is going through.
There you go. We now have 15 total shares. You can see here what that looks like on here. We have 15 shares. Dolphins right now has eight from other users. If I were to win here, my winning would be 23 shares. You can see here at the bottom, it shows based on what you have purchased in that market, what your shares are based on the outcome. So, over here, you can see all of that down here. And that's pretty much the card here: expiration date, the market itself, what the market looks like between users purchasing shares, the ability to purchase those shares, and then your shares, and then the outcomes based on the resolved results of the market. That is the example of an active market.
Now, we have a pending market right here, right? This one is is 100 a number. You can see here, yes. And then no. If I if yes wins, I should be able to win 14 shares here. Now, I don't have any UI built into the application to resolve markets, but we can do that directly through the contract itself. So, if I come over to the contract here, I have my prediction market contract. I can then come to the Explorer tab here, and I'm going to go to the read functions first, and I'm going to get market info. I have to make sure I resolve the right market here. I believe it's token ID 8. Yes. So, here is 100 a number, and what we're going to do is resolve market ID number eight.
So, if I come to the write functions here, I can resolve a market. I can say market ID 8, and then we have the outcome. So, the outcome is a uint8, so we're either going to put in a one or a two for option one or two of our market. Now, our result here should be option one because yes is option one, and is 100 a number should be yes. So, put in option one here, and we'll execute. We'll confirm that transaction. This is going to resolve that market and then put it into the resolved section, then allowing users to claim their rewards if they had the winning result. So, that went through here. We'll come back here. You can see our pending section no longer has that pending market. If we go to resolved, though, we have here is 100 a number. The resolved answer is a yes. You can see here, we do have 14 shares that we have to claim here.
And if I come up to the top really quick, you can see in this wallet I have 472 predict tokens. So, plus 14, I should have 486 after I claim my rewards. So, I can come down here, I can claim my rewards. You see it's claiming, and it's going to claim my 14 shares from this prize pool here. So, rewards have been claimed. If I come up to the top here, there you go. I have 486 predict tokens now. So, again, because I have rewards there now, if I try to claim it again, it's going to say I have no winnings, because again, our smart contract is going to check and make sure that that user has rewards to actually be claimed. And that is our simple prediction market demo. This is what we're going to be building in this tutorial, and again, we'll be building this very simple version of a prediction market. We'll build the smart contract from scratch, we'll build out the application, and everything, all using Thirdweb's tools.
But before we jump into that, we'll quickly look at what prediction markets are, the popular rise of prediction markets like Poly Market, why they became such a big thing, especially after the 2024 US presidential election, and how people are actually using these prediction markets. So, we'll go over through some use cases, how they work, what are they, and then we'll jump into building out the application ourselves. If you want to skip over that part, we'll add timestamps down in the description below. You can just jump right into the tutorial if you want to just skip to the building. Again, before we jump into creating the smart contract and then the application that we just demoed, let's kind of just talk about prediction markets themselves. What are they? How they work? And what we're going to be building in our application.
So, if we look at this, the market itself, right? I was using the term market in our demo. The market, we can at as a card here, and we have a few different pieces of details that we're going to be displaying in this market. First is the expiration date. This is when a market closes and is no longer accepting share purchases. So, of course, with prediction markets, they are predicting the future result of a future event, and this means that there has to be an expiration date of when this market is going to close, because we should know the result of that future event, and then the market can then be resolved, and people can then claim their rewards based on the results. So, we need an expiration date of that future event of when the market will be closed, and the market will then be resolved.
We then have the question, right? This is the market question. Who's going to win the US presidential election in 2024? Is 100 a number? Which, you know, sport team is going to win A or B? And then you have your choices here. Now, depending on your prediction market, you can actually create more multiple choices if you want, and all those choices, again, will be options that people can then purchase shares for. In our example, simple prediction market app that we're going to be building, we're just going to allow users to provide two different options, option A and option B, and then they can, of course, choose which one, and we resolve to one of them at the end. So, ideally, that's the bare minimum you're going to need for creating a market. And again, these markets, you can have multiple markets within your application itself, and the one thing is, of course, they need to have the actual question itself, the expiration of when that future event should have its result, and then the options that users can then choose.
Now, there's a few different models you can base your prediction market off of. In our simple application that we just demoed, we use something called a fixed model. It's very easy, a very simplified version of a prediction market where each share costs one, and it is a set price, right? One predict token equals one share that the user could purchase of the option that they chose. And then at the end of that, the winning options, the winning option splits the shares of the losing option. So, we have an example here, just so we can show like calculations of how this works. If user one buys 10 shares of option A, and then user two buys two shares of option A, and then user three buys six shares of option B, we have 12 shares of option A and six shares of option B. So, let's just say we resolved this market, and option A was the correct result here. So, user one and two will now split the shares of option B, which is six shares here. Now, it is a 12-6 ratio here, meaning that each winning share of option A gets 0.5 or half of a share. This means user one, because they put in 10 shares here, they get 0.5 for each one of the shares that they purchased, which means they get a total return of 15 shares here. And then user two, because they put in two shares here, they get a total of one extra share of profit here, to giving them a total of three in return. So, again, this is still the total of 18 shares, which was here. And here, it's just the losing shares get divvied up equally into the winning shares. And that's really what a fixed model is. It's a very simplified version of a prediction market, and what we're going to be building in our tutorial.
Now, we can look at a different model, and this is going to be a model that a popular prediction market like Poly Market is going to use, and that's more of an order book model. And if we take a look at Poly Market really quick, you can see here what Poly Market does is if we select one here, like say, "Will Bitcoin hit 100K in 2024?" You can see we can purchase yes or no. And right now, yes costs us 66 cents, no, it's going to cost us 35 cents. And this is more of an order book style, and you could relate this a little bit more to like say, stock trading. Right? A user can set the price in which they want to buy and sell, and that order doesn't go through until someone matches it. So, with an order book model like Poly Market, users can buy and sell shares at any time, versus the fixed market, you're just buying shares until the market resolves, you'll then be able to claim the shares if you had the winning result. Versus an order book model, like again, you can compare it more to like stock trading, you can buy and sell shares before anytime before the market actually resolves, and prices are determined by what people are buying and selling at. And I can put in a certain price for a transaction, and it's not processed until someone matches the offer of what I am putting up my shares for.
And another model that you can look at as well is something like automated market makers, where you can set up maybe liquidity pools for each one of the shares, and prices and everything adjust depending on how much people are buying of one share or another. So, those are some of the other models that you can build your prediction market around. Again, we are going to be building with the fixed model, which is a very simple version of a prediction market, and then you can, of course, look into those other models like the order book model that Poly Market and other prediction markets use.
And another prediction market application I want to throw out there is Myriad. They're beta right now, but you can check them out. We'll put the links and everything down in the description. But this is more of a gamified prediction market where you can earn points or tokens, kind of like in our example, through doing different quests, doing collecting your daily streaks, you can do different tasks and everything to collect points, and then you can actually use those points on the prediction market and place your and purchase shares for different markets and earn more points. So, it's a really cool and creative way to get your community to interact with your type of content. Really cool application, highly suggest checking it out if you know you're looking to integrate prediction markets into your app or your community.
And that was a quick overview of what a prediction market, some of the different models, what we're going to be building in our tutorial here. And the next thing we're going to do is start off with our smart contract. We're going to build it out from scratch and then build our application to interact with it.
Now, let's start with building out our simple prediction market smart contract. Now, I'm in my terminal here, and I'm going to create a new smart contract using Thirdweb's CLI tools here. So, I'm going to run `npx thirdweb create contract` here. From here, we can now name our project. I'm just going to call this a PM contract here for prediction market contract. You can select the framework you want to use. You can select Hardhat or Forge. I'm going to be selecting Forge here for this example. And then we're going to name our new smart contract here. So, I'm going to say "simple". From here, you can select the contract that you want to start from. You can select from pre-built contracts like ERC721, ERC20, or 1155, but we're building out our own smart contract from scratch. So, I'm going to select "empty contract" here.
Then, we are going to just change into our prediction market contract project and I'm just going to clear this up to the top. And then we're going to open this up in our editor. I'm going to be using Cursor in this video, so I'm just going to open this up in Cursor really quick. And once you have your project open here, now, if you are using Foundry, you should have a `src` folder that has your contract.solidity file here. If you're using Hardhat, I believe there's a `contracts` folder, and then you'll have your contract.solidity file in there.
Here is our Solidity contract here. I'm going to just rename this here to our `SimplePredictionMarket`. Name our contract here `SimplePredictionMarket` as well. And then the first thing we are going to do is import some of the contracts that we're going to be using here. I'm going to first import our `IERC20` here from our `IERC20.sol` file from Thirdweb. And this is because we are going to be implementing, again, our very own custom ERC20 token that is used as the token that users can use to purchase shares in the market. So, we're going to need access to our own custom ERC20 token. We're also going to import `Ownable` from Thirdweb contracts extensions, `Ownable` here. And this is going to allow us to set the owner of the contract, and this is going to allow us to make sure that only the owner can execute certain functions within the contract, like creating markets or resolving markets that shouldn't be open to everyone to be able to do. We'll also make sure that we add that this contract is of `Ownable` as well here.
And then the last one we are going to add here is our `ReentrancyGuard` from Thirdweb here. And again, we'll link down in the description below a link to the repo of this smart contract as well. So, if you want to get these imports here, you can copy it from that repo here. And again, we are adding the `ReentrancyGuard` here just to add reentrancy guard to to our smart contract, and we'll also add that here to make sure our smart contract inherits those.
And then the first thing we're going to add into our contract is an enum here for our `MarketOutcome`. This should only have `Unresolved`, `OptionA`, or `OptionB` depending on what status the market is currently in. If it's been resolved, it should be `OptionA` or `OptionB`, but if it's still currently not resolved, then we should have `Unresolved` here.
We have our `struct` for our `Market`, which is going to contain stuff like the question of the market, a `uint256` for the end time or the expiration of when the market should end, the `MarketOutcome`, which is the `MarketOutcome` up here that we are talking about. We have our two options, again, in this simplified version of our prediction market, we're only allowing two options per market. And then we have `uint256`s to track the total `OptionA` shares and total `OptionB` shares. We have a `bool` here to see if the market is resolved or not. And then we have mapping for `OptionA` balances and `OptionB` balances, so we can look up a user's address to see how many options and shares they have of each option here. And then we have a mapping here to show if an address has claimed their reward or not. So, that is going to be our `Market` struct here.
Next, we're going to create some variables here. One is going to be our `bettingToken`. So, we're going to set this to our `IERC20` here, which is what we we imported right up top here. We have our `marketCount`, which is going to be a `uint256`. This is going to keep track of how many markets we have within our contract here. And then we have our `markets`, which is going to be like a market index to the market itself, so we can search a market ID number and it should return us back the market struct of the information of that market.
Next, we are going to have our events here. And our events are going to be our `MarketCreated`. This is going to give us the market ID, the question, the options A and B, and then the expiration and end time. We'll have events for `SharesPurchased`. This is going to be a `uint256` of the ID of the market that the shares were purchased from, who the buyer was, what option they purchased, and then the amount that they purchased here. We'll also have `MarketResolved`, and this is for when a market is resolved. We'll return an event with the market ID and the outcome itself. And then we'll have an event for `Claimed`. This is going to be when a user claims their rewards from a specific market. So, we'll have that information right over here.
Next, we'll set up our constructor here. Our constructor, again, is going to be that function that runs one time when we deploy our smart contract. What this is going to take is one parameter here, which is our `bettingToken` address. So, again, we're going to deploy our very own custom ERC20 token. We're going to have to provide that token address here to our contract when we deploy it. That's going to set the `bettingToken`, which is the variable that we created right up here. It's going to set the `bettingToken` to our `bettingToken` that we provide in the contract parameters when we deploy it. And then we're also going to set up the owner of the contract to the deployer of the contract. So, whoever the `msg.sender` is, is going to become the owner here. And that `setupOwner` function is coming from our `Ownable` contract right here.
Next, we're going to create our first function here, which is going to be our `createMarket` function. This is going to require a few things: one, the question of the market, so what is the market going to be about? And then we have to have a string for `optionA` and `optionB`, and then our `duration`. Now, our `duration` is going to be provided in seconds for our contract here. So, this is the information we need in order to create a market. Now, we are going to require that the `msg.sender` or the person who can call this function is the owner. They have to be the owner in order to create markets. So, that's one check here. The other one is the `duration` needs to be greater than zero. We can't have a negative or zero duration because then the market would just expire immediately. Again, that is going to be in seconds that we provided. And then we're just going to require here that we have something entered in for `optionA` and `optionB`. So, we're making sure that the length is greater than zero, so there has to be some kind of option there for a user to be able to select from when a market's created.
We're going to create a `marketID`, which is going to be the `marketCount` plus one. We're then going to store that market here into the `marketID` index. And then we're setting the market question to the question provided, the options to the option provided. We're adding the end time, which is going to be the current block timestamp plus the duration that was provided. And then we're going to set the outcome by default, it is going to be `Unresolved` until the market is resolved. We'll set `optionA` or `OptionB` from there. And then we're going to emit the event that the market was created with the following information and then return our `marketID`.
Next function we're creating is the `buyShares` function. This is going to allow, I'll close this real quick, allow our users to buy shares of a specific market. So, we're going to require that the `marketID` is provided, the `option` in which of which option we are going to be purchasing. We are going to set it as a `bool` because it is either going to be `OptionA` or not `OptionA`. And then we're going to set the `amount` here as a `uint256`. So, we need to know what market, what option, and how much shares they are purchasing. This going to be an external function, and we're going to get the market here by the `marketID` that is provided. We're going to to require that the block timestamp is less than the market end timestamp here. We want to make sure that the expiration date has not passed yet. We're going to make sure that the market also is not resolved, because if the market's resolved, it means it's closed already. You shouldn't be able to purchase shares from it. And we're also going to require that the amount that the user wants to purchase or buy shares is greater than zero, right? They have to purchase a positive number of shares here.
And then we are also going to require here that the `bettingToken` we can transfer the balance and amount that they are looking to purchase. We want to make sure that that goes through over here, that we can transfer from the `msg.sender` to this address the amount that was asked for. If not, we'll say the transfer failed. And then finally here, if it is `OptionA` that the user purchased, we're going to make sure that the address here, we show the amount that they purchased, and then we'll add the total `OptionA` share, a share will add the amount that was purchased as well. So, this is tracking the user's `OptionA` shares, and this is tracking the market `OptionA` shares. And then else, we do the same for `OptionB`. And then finally, we emit that the shares have been purchased, use emitting the `SharesPurchased` event over here and providing it the information.
Next, we have our `resolveMarket` function, and this is for once a market has expired, we can now resolve the market and set the outcome of the market and allow users then to claim their rewards. So, we're going to need the `marketID` that we're resolving and then the `outcome` of the market here. And we're going to one, require that again, only the owner of the contract should be able to resolve markets here. We are going to then get our market by providing it the `marketID` that was provided. We're going to require that our block timestamp is greater than the market end time, again, checking to make sure that the market should have ended already and it should be expired. Then we're going to also make sure that the market hasn't been resolved, because we can't resolve the market twice. So, if it's not resolved, is what we're looking for. If it's been resolved already, then we're going to say "Market has already been resolved." And then we're going to make sure that the `outcome` does not equal `Unresolved`. So, we have to provide a index number for the `outcome` here. So, if we look back up here at our enum for `MarketOutcome`, `Unresolved` is index zero, `OptionA` index one, and `OptionB` is going to be index two. So, we're setting that to our `resolveMarket` here, and we're just making sure that we're not setting it back to `Unresolved`.
Then we're going to set the the market `outcome` to the `outcome` that was provided, and then we're changing that the market `resolved` has been resolved, so we're setting that to `true`. And then we're emit the `MarketResolved` event and provide it the `marketID` and the `outcome`.
Next function we have is our `claimWinnings` function here. We're going to have to provide the `marketID` that we're claiming the winnings from. So, we'll get that market information by getting the `marketInfo` here with the `marketID` that was passed. And then all we're going to do is require that the market has been resolved, right? We have to make sure the market is resolved for a user to claim their rewards. We'll set some variables here for `userShares`, `winningShares`, and `losingShares`. And if the market `outcome` is equal to `OptionA`, we're going to look up what the user shares are by providing the user's address, looking up through the mapping, which is in our struct here, again, we are looking up at the user's balances for each one of those shares, and we're setting the `userShares` variable here to however much shares they had depending on the the market `outcome`. We'll set `winningShares` to the total amount of shares, and then the `losingShares` to the total amount of of the other shares.
And once we set the `userShares` here variable within this function, we are going to set the user shares balance within the struct here to zero to make sure that that resets back down to zero. So, we're going to require that `userShares` is greater than zero, because if it's not greater than zero, they have no winnings to claim. So, we're going to require that `userShares` is greater than zero. We'll calculate the `rewardRatio` here. So, again, the losing shares get the divvied up into the winning shares. And we'll calculate the user's winnings by calculating what that ratio was and multiplying it by the amount of shares that they actually have within that market here. And then we're going to require here that the transfer of that `bettingToken` gets sent to the user, the `msg.sender`, they get sent the number of winnings that was calculated based off the shares that they had. And then we will emit the `Claimed` event with the `marketID`, the sender, and their winnings.
And that does it for the main functionality of our smart contract here, right? We have our ability to create a market, we have the ability to buy shares of a market, we have the ability to resolve a market once it has expired, and then claim winnings based off of what shares you purchased within the market. Next, we're going to create a few functions here to get some information and data from our smart contract that we can display within our application. The first function we're going to create for that is a `getMarketInfo`, which is going to take a `marketID`, and it will then return us information of that market, which include, you can see here, it's going to return us a string for our question, our two options here, the end time, the current outcome, and then the total option shares for A and B, and if the market has been resolved or not. So, what we're going to do here is look through our `markets` here with the `marketID` and return that information that we asked for for that market. So, again, we can specify the market with the `marketID` and get returned that result over here. And this is going to allow us to one, get the total market count, and then map through the markets here, get the information, and then display it in our application.
And then what we're going to do here is create a function for a `getSharesBalance`. This is just going to return the balance of a certain market based off a certain user. So, we can provide it a `marketID` and the user's address, and it should show us what their balance for `OptionA` shares and `OptionB` shares are. So, over here, you can see it returns us the `uint256` of `OptionA` shares and the `uint256` of `OptionB` shares. Again, we'll look up the market information with the `marketID` that's provided, and then we'll return the the market `OptionA` shares balance of that specific user, and the same thing goes for the `OptionB` shares as well.
And then that does it for our simple prediction market smart contract. Again, we're able to create a market, we're able to buy shares of a market, resolve a market, and claim our winnings, along with getting information of a specific market based off the ID and getting the share balance of a specific market for a specific user. Now, in the repo below that, we have we also have functionality for things like batch claim winnings, if you want to just be able to claim and distribute all the winnings for the users that purchase shares in that market. We're not going to add that into this example here, but you can check out the repo down in the link below and check out and see how we implemented and added that function in here. We have in this example our one `claimWinnings` function where an individual user can claim their winnings from a specific market.
And then there is one more function that we need to add and create here. This is for overriding the `canSetOnly` functionality from the `Ownable` contract that we imported here. So, I'm just going to add this right above our constructor here. And again, this function here is the `canSetOwner`, and we're basically going to override this function and make sure that the `msg.sender` is equal to the owner here.
And now that we have our contract finished, we can now deploy this contract. And Thirdweb makes it super easy when you use our CLI to deploy these contracts, and you don't have to expose things like your private keys or anything. You can actually deploy directly through Thirdweb's dashboard. So, I'm going to open up my terminal here for my project, and in this terminal, I'm going to run `npx thirdweb build` first, just to make sure our project here compiles properly. You can see it detects that we are using Foundry, it's compiling our project. There you go. You can see our compilation has been successful. It does detect our `Ownable` and `Fallback` extensions here. It also suggests that we can use our `Permissions` and `ContractMetadata` extensions as well. This is going to allow you to set permissions for the contract, or set things like a an image name and description for the contract as well with `ContractMetadata`. You can choose to add those if you if you want to. We're not going to add it in this example smart contract here.
So, once we know that it does compile, we're going to run `npx thirdweb deploy`. And when you run `npx thirdweb deploy`, you're going to see that it's going to ask you to include the `DK` flag with your secret key. Now, the secret key, you can get from Thirdweb's dashboard, and that if you come to throw the dashboard here, you can create your secret key, which is going to be your project secret key. So, once you are connected with your wallet to the dashboard, you can head over to `Projects`, we can add a new project here, and we're going to create a new project. We're going to call this our `SimplePredictionMarket` for this tutorial. We're just going to allow all domains, but you can add the list of domains that you want to whitelist for your API keys here. And we're just going to again, allow all domains here. We're going to hit create, and I'm going to hit proceed. And then I'm going to be given a client ID along with a secret key. And this secret key, we can copy here. You can store it safely on the side, read the warnings and everything here, and then we're going to just check this off and hit complete.
And then what we can do is come back to our project here. Make sure that we write `npx thirdweb deploy`. Of course, you shouldn't be sharing your secret key. I'm going to delete my secret key that I'm using here, but we put `DK` and then provide our secret key here. And you can see that will now deploy our contract. And what this is going to do is compile our contract and then open up this contract deployment in our Thirdweb dashboard. So, once this is done, you'll get a browser window that will open up, and you can see here, we can now deploy our `SimplePredictionMarket` contract directly through Thirdweb's dashboard.
Now, our contract is going to ask for a `bettingToken`. If you remember, we're setting our own custom ERC20 token to this contract here. What we'll do now is just we'll easily deploy an ERC20 token through Thirdweb. So, I'm going to come back up to my Thirdweb dashboard. I just switched to a different tab here. I'm going to come to the `Contracts` tab. I'm going to hit `Deploy a Contract`. And this is going to open up our contract explorer page. You can of course see all the different pre-built audited smart contracts that you can deploy here. We're actually going to come into this popular section and select the `Token` contract. This is an ERC20 contract here. You can read more about it, you can view all the events, functions, source code, and everything for the contract if you want to. We're just going to come up here to the `Deploy Now`. And I'm going to name it here. I'm going to name this our prediction token. We're going to give it a symbol here of prediction market token, PMT. And then you can add a description and an image. This is the contract metadata that we were talking about earlier, but we don't need to mess with any of that. We're just going to give it a name and a token so that we can display that symbol there in our application. You can of course set everything else here and then set the chain that you want to deploy to. Select this dropdown and search any EVM compatible network. We support mainnets, testnets, whatever you're looking for. You can search the network or chain ID and select the chain that you want to deploy to. For this tutorial, I'm going to be deploying to the Base Sepolia testnet. I'm going to select `Deploy Now`. I'm going to get a pop-up here to confirm the gas and everything for the transaction. So, I'm going to hit confirm. That's going to deploy our contract. We'll get another pop-up for a signature here to add the contract to our Thirdweb dashboard. This is just a signature request, you're not paying from gas or anything on here. We're just going to confirm. That's going to add it to our Thirdweb dashboard, and then we'll be able to use Thirdweb's contract dashboard to be able to interact with our contract here. Once that finishes, we can click on `View Contract`. That's going to bring us to the contract dashboard here. You can see this is our contract, contract for the prediction token deployed on the Base Sepolia testnet. And we're going to be given our contract address here. So, I'm going to click on that. That's what we're going to use. We're going to come back to the tab where we're deploying our `SimplePredictionMarket` contract, and I'm going to paste in that contract address for our betting token.
Now, I'm going to select the chain I want to deploy to. I'm going to make sure that the same chain that I deployed my token to is the same chain that I deploy my prediction market contract to. And I'm going to hit `Deploy Now`. We'll confirm that to deploy our contract. And just like we did our ERC20 token, we're going to deploy our contract, add it to our contract dashboard here, and then we'll be able to view our dashboard. There we go. It has been deployed. We'll view our contract. And now we should be able to see our `SimplePredictionMarket` contract, again, deployed on the Base Sepolia testnet. And then we have our contract address here. So, if we come over to the `Explorer` tab here, we can look at all the write and read functions from the contract that we just created. We look at our `Read` here, we can see our `bettingToken`. This is going to give us our custom ERC20 token address here. We can `getMarketInfo`. If I put in something like a zero and I hit run, not going to give us any information back because we don't have any markets created. We can `getSharesBalance` for a specific market ID and user. We have our `marketCount`, which right now is set to zero. And then we have our `markets`, which we can of course look up again through the `marketID`. And then we can view the `owner`, which is the wallet address that I deployed this with.
We come to the `Write` functions here, we have our write functions to `buyShares`, `claimWinnings`, `createMarket`, `resolveMarket`, and we can also `setNewOwner` if we have a want to set the new owner for our contract here. What we'll do now is create our very first market. We'll first create our question. So, we'll say, "Will Bitcoin hit 100K by tomorrow?" We'll do that. We'll say `OptionA` is yes, and `OptionB` is no. And then we'll set the duration. And we'll just set this for a day. So, in seconds, we'll set this to 86,400, giving us 24 hours here. And that is the information we're going to need to again, create the market. We need when it's going to expire, the question of our market, and the two options that users can choose from. We'll come here, we'll execute this, and we will have to pay the gas here to execute that transaction. And then we should be able to then read some of this information here from our smart contract.
`CreateMarket` has gone through. We can come back to `Read` here. We can look at `marketCount`. You can see `marketCount` is now at one. We can `getMarketInfo`, and we're going to give it the ID of zero or index zero, and then that will return us back our information of our question, our two options here, the timestamp of the expiration date, and then we have the total shares here, and then we have the information of everything like our total shares and if the market has been resolved. And we can then now pull this information in our application and display it in cards or however we want to display it in our app, which is what we're going to build in just a bit.
All right, and we've deployed our very. All right, so we've created and deployed our simple prediction market smart contract. We deployed our own custom ERC20 that we're going to use as kind of the token to purchase shares from our prediction market. Now that we have those things, we can start building our application, and we're going to be using Thirdweb and our Connect SDK to do this. So, I'm going to create a new project. I'm going to come back to my terminal here. Let's just clear out of this, and I'm going to create a new Next project here. So, I'm going to run `npx create-next-app` and I'm going to call this `prediction-market-tutorial`, so `PM-tutorial`. You can name your project whatever you want. We're just going to create a new Next project here. We're going to say yes to TypeScript, ESLint, and all the defaults here. So, we're going to say yes to source directory and app router, no to Turbopack, and no to customizing the import alias.
And once that's done, we'll change into our `PM-tutorial` project. Just, oops, clear this here. And once we're in there, we're going to import a couple things. We're first going to import Shadcn UI, which is what we're going to be using for our UI. And we'll link a link to what Shadcn UI is in the description below. But if you're familiar with it, well, we're just going to install that right now. So, we're going to say `@shadcn/ui` or we're going to run `npx shadcn-ui@latest` and we're going to say `init`. And we're going to set this to the default and neutral. And we'll say yes to use CSS variables for theming. We're going to use the Legacy peer dependencies here. There we go. So, we've installed Shadcn UI. And then we're going to install Thirdweb. So, we're going to run `npm install thirdweb` here. And this is going to install Thirdweb to our project. We'll also link the documentation down below so you can follow along there on how to install Shadcn.
CN and how to install third web as well, or you can just follow us along in this tutorial and we'll walk through all of it.
Once we have third web installed, I forgot which shads in, we do need to add the component to our project as well. So I'm going to run `npx shadcn-ui@latest add` and we're going to run `add` here. This is going to give us all the different shadcn-ui components that we can add to our project. We are going to add the badge, the button, a card, and you can just, just of course, go use your up and down arrows and use the space bar to select. Uh, you can see that's when it says "all" over here. Uh, so badge, button, card, uh, we have input that we need, uh, progress, and, uh, tabs. So those are the ones that we are going to import.
Once you have them selected, you can just hit enter, and we'll use the Legacy peer dependencies here. And then this is going to add those components, uh, to our project here so that we can use them. But once we have all of that there, I'm going to open this up in my editor again. I'm going to be using Cursor.
And once we have our project open, we're going to go into the `src` folder and we're going to set up and configure our third web SDK here. Uh, in the `app` folder here, I'm going to create a new file. We're going to call this file our `client.ts` file. And what I'm going to do is come over to our third web docs here. We'll link this down in the description below as well. And under our Connect SDK, I'm going to go to the React portion. We'll hit "Getting Started," and in here you can see and go through the steps on how to get third web set up and configured for your application.
And first thing we're going to need to do here, you can see, is set up our third web provider. So we're going to have to wrap our, uh, application with the third web provider component here. And then we're going to have to create this client here. And this is why we created the `client.ts` file. I'm just going to copy this code snippet here, come back to that `client.ts` file, and then paste that in. Um, we're going to say "as string" because we are using TypeScript here, and we'll save that. Uh, again, that first step was wrapping our application with the third web provider component. I'll come to the `layout.tsx` here and then our children here. Um, I'm going to make sure that we wrap this here in our third web provider. Uh, we don't need to provide `client` here, just wrapping the third web provider around our children of our application. And then I'm going to come back up to the top here and make sure I import `ThirdwebProvider` from `thirdweb/react`.
Now, if I come back to this `client.ts` file here, you can see we're creating our client using `createThirdwebClient`, and we're providing it a client ID, which we're storing in our `.env` file, um, with this variable here, uh, which is what we need to create. So we're going to create a new file in the root of our directory here, and I'm going to say `.env`. Uh, we'll make sure that we have `.env` in our `.gitignore` here, so I'm just going to put that there. And then we will get the variable name from our `client.ts` file and we'll paste that into our `.env` file here. And then we're going to get our client ID from our third web dashboard. So if you remember, we created a project, uh, which was our client ID. We're going to come back to our third web dashboard. We're going to go to the "Projects" section. We look for our project that we just created, which is this "Simple Prediction Market." I'm going to copy my client ID there, come back here, and then I'm going to paste that in for our variable for our `NEXT_PUBLIC_THIRDWEB_CLIENT_ID`.
And just like that, we have configured our Next.js project with our third web Connect SDK, we wrapped our application with the third web provider, and we have created a client here, which is going to allow us to start accessing, um, different components from the SDK here.
Next, in the `src` folder, I'm going to create another folder here. We're going to name this `constants`. And in that `constants`, I'm going to create a `contracts.ts` file. And we're going to store all our information for our different contracts here. So in here, we're going to export a couple variables. Uh, we're going to export a variable for our prediction market contract address. We're going to export a constant for our token contract address. And we're going to get those contract addresses from our dashboard. So we'll come back here, we'll get our token contract address, come in here, paste that. And then we'll get our prediction market contract address, which is right here. We'll copy that and paste that in.
Then we are going to create wrappers for these contracts here. And we're going to create them using, uh, the `getContract` method here. So we're going to say, oh, sorry, uh, export a `const` here called `predictionMarketContract`. And we're going to use the `getContract` method, uh, which is going to create a wrapper for our contract to allow us to start interacting with that contract in our application here. We need to provide this with a client, which is going to be the client that we created in that `client.ts` file. We'll provide it the chain that we have deployed these contracts to. We're using a predefined chain from the SDK, so I can say `baseSepolia` here, uh, and then we can import that here from `thirdweb/chains`. If you're not using one of the predefined chains from the third web SDK, you can use the `defineChain` hook here, and then you can just provide it the chain ID of the chain that you are going to be using. And then in, uh, we'll have to then provide it the address, which is going to be the prediction market contract address. So that's going to create the wrapper for our prediction market contract. And then we're going to create one for our token contract, which is going to be the exact same thing, just providing it the token contract for the address.
All right, and then next thing we're going to do is we're going to create in this `components` folder here. We're going to create a new file and we're going to call this our `PredictionMarketDashboard.tsx`. Right? And then in this `PredictionMarketDashboard` component here, we're going to, uh, say `export` here, and we're going to create our prediction market component. Uh, we're going to start off with the dashboard here. So we're going to just come here and we're going to add a couple divs here. All we're doing for this div is setting the height of the container here, and then creating a div within that. Again, if you want to look up all the styling and everything that we're doing, I will link a link down to the repo down in the description below so you can check that out if you want to copy the exact same thing that we are creating here. Um, I'm going to kind of go through quickly the styling stuff, but I'll spend some time going over the actual functionality, uh, getting the data and everything from our smart contracts.
So next, we're going to create all the different components of our dashboard here. So if we look back at our demo here, we have, first thing is our navbar up here, which should show the name of our application along with a connect wallet button, which allows a user to sign in with a web2 off method, create them a smart wallet using account abstraction. Uh, we'll also add in this "Claim Tokens" button utilizing Engine here. That claim, uh, tokens button is an optional feature. Uh, we just are adding it into the demo here so that we actually have tokens that we can use within our application. Uh, but we'll create that navbar first. So we're going to create a new file in our `components` folder. We'll call this `Navbar.tsx`. And we're going to create our navbar here. And then for our navbar, we're going to have a div here that's going to have a flex container here with a `justify-between` for the items within it. We're going to have our title here, which is going to be the title of our "Simple Prediction Market." And then we're going to have a div here which is going to contain all the other components like our connect wallet button and the claim tokens button.
We'll first work on the connect wallet button here. And with third web's Connect SDK, it's super easy to add this functionality in. We're just going to add the `ConnectButton` component here, and we provide that with our client, uh, which is going to be the client from our `client.ts` file that we created. And with that, uh, we can actually just come back to our dashboard here, and in here, uh, add our navbar. Make sure we import that here and save that. And then we'll come back to our `page.tsx` file, uh, in our `app` folder, `page.tsx`. We'll delete all this, um, default stuff here. And what we want is our `PredictionMarketDashboard` component as our main component here. So we have that.
And now let's just run this application here. I'm going to open up my terminal here and run `npm run dev`. And what we should see is our navbar up here. And there you go. So you see our "Simple Prediction Market" title. We have a connect button here. Now, the cool thing again is with third web, with just that `ConnectButton` component, you're going to get a fully functioning connect button where we can connect a wallet to our application and everything. By default, it shows us our in-app wallet options, but I can connect like a MetaMask wallet here, and it's going to connect my MetaMask wallet to our application. Uh, but we're going to do a little bit of customization to this button. And again, we're going to allow a really easy onboarding process for people who are familiar with web3 applications, aren't familiar with wallets. We can again create an in-app wallet sign-in option where a user can use an off method they're familiar with, like Google or an email or a passkey. And we're also going to create them a smart wallet using account abstraction, which is going to allow us to again enable features like gas sponsorship, so a user doesn't have to pay for gas on our application here.
And what you can do is you can actually come to, uh, `playground.thirdweb.com`. It's a great way to one, use a lot and test out and see how all these different tools of third web works. But if you come to the "Sign In" and you come to the "Button" here, you can actually play around with the customization of the button here. See what it's visually going to look like in this visual editor. I can say, "Hey, do I want in-app wallets? Do I not want in-app wallets? What sign-ins do I want? What wallets do I want to show?" You can change the appearance, the text, uh, the color, all of that you can customize here. The visual editor will show you what it looks like, and then you can simply just come to the "Code" tab here and then copy and paste the code from here into your application. So the playground is a super good place to get started with. It's fun to just play around and see what you can build with the `ConnectButton`.
But I'm going to come back to my application here. Let's head back to the `Navbar` component. Uh, but for the `ConnectButton`, I'm going to add a few things. One, I'm going to set the `theme` here to the `light` theme. This is just because we are using a light theme for the application, so I'm going to set it to that. I am going to set the, the `chain` here to `baseSepolia`. And this is so by default, this is the chain that the `ConnectButton` is going to connect our user's wallet to. Then we're going to customize the `ConnectButton` itself. And with the `ConnectButton`, I can, uh, customize the `label` here. So instead of saying "Connect," I can have it say something like "Sign In." And then I'm going to give it some styling here as well. And I'm just going to give it a styling of `fontSize` and adjust the height of the button.
So if I come back to our demo here, you can see I have the "Sign In" button here. And now instead of saying "Connect," it says "Sign In," and we have a light themed, uh, modal here, or our wallet options. I want to set the `wallets` to only be allowed to show in-app wallets here. So I can say `wallets` provided an array of the wallets, but in this case, it's just going to show `inAppWallet` here. And at the top, I can import `inAppWallet` from `thirdweb/wallets`. If I save that and come back here, now if I hit "Sign In," oh, uh, directly to a client component. Got it. So we have to come back here really quick. Uh, we have to make sure that we use `useClient` here. And then we'll come back and then just refresh this. There you go. So we have our "Sign In" button. When I click on it, you can see we only have our in-app wallet options. We don't have the ability to connect an external EOA wallet like a MetaMask or something. Uh, it's only going to show in-app wallet options here.
And then we will come back here. And the next thing we want to do is again, we want to be able to use smart wallets and account abstraction to enable things like gas sponsorship. And with third web's `ConnectButton`, it's super easy to implement this. All we need to do is add the `accountAbstraction` property here. We specify the chain that we want to have account abstraction available on, in this case `baseSepolia`. And then if we want to sponsor gas or not. So we hit `sponsorGas` and we set that to `true`. And with just those few lines of code, we've enabled account abstraction and smart wallets on our application via the `ConnectButton` component. So save that. And and if we come here back to our application, and I hit "Sign In," let's do Passkey here. I'll create a new Passkey, sign in using a Passkey, and then that's going to create me a smart account. And you can see here, this is my new smart account, my new wallet address that I have been generated for this application here. Um, I don't have any, uh, ETH or funds in this wallet. It's a brand new wallet. And you can see here, uh, we have zero funds.
Now, one of the cool things with this, uh, `ConnectButton` is again, we have a lot of customization that we can do on it. So what I'm going to do here is instead of showing the native token of ETH, I'm going to show my token balance for our predict token that we just deployed. So what we can do is come back to our `ConnectButton` component here, and we can configure the `detailsButton`. And in the `detailsButton`, we can configure the `displayBalanceToken`. And we can set this to, uh, when we're on `baseSepolia`. There you go. So we, we can set the token, uh, default token balance shown to a specific token. So we're going to get the here, the token contract address that we stored in our `contracts.ts` file. So what this means is now, if I come back here, instead of showing the ETH balance of my wallet that was created, I'm going to show the PMT, or our prediction market token that we deployed, we're going to show that balance instead.
In this case, zero. Now, I could copy this here and just for this here, let's come back to our prediction token and let's go to "Tokens" here, and I'm going to mint some tokens. I'm just going to mint, uh, 100 here to my wallet. There you go. So we, and we're just going to mint some tokens and send it to the wallet here, that way we can show that, you know, it's displaying the right values. So we hit "Transfer," and we're going to transfer it to the wallet address of that wallet that was generated for us. And let's just say 20 tokens is what we're going to transfer. There you go. So we have from this here, it's going to send 20 PMT over to that wallet. And once that's done, we can check our `ConnectButton` to see if we actually have the 20 PMT there. So that looks like it's sent. We'll come back to our application here, and there you go. You can see we now have 20 PMT, and that's the default balance that's shown on our `ConnectButton` instead of the native ETH token on the selected network, or whatever the native token is on the selected network that we choose.
Awesome. Now, the next thing we can do is again, there's that demo "Claim Token" button that we created in our demo application. We're going to create it really quick, and this will actually be a quick introduction on using third web's Engine if you aren't familiar with it already. Uh, we're just going to implement a button here that is going to make an API call to Engine, which is going to be our blockchain API, which is going to then execute and claim some tokens, uh, and send it to our user's address here. Now, we only want to show that button, of course, if a user actually has a wallet connected. When they're disconnected, we shouldn't show that button at all. So what we'll do is come back to our application here, and in order to check if we have a wallet connected or not to our app, we can create an `account` variable here, and we're going to use the `useActiveAccount` hook. And what the `useActiveAccount` hook does is it returns us back the connected account to our application, or it returns us back `undefined` if there is no account connected. So with that, we can actually say, "Hey, if there is an account, then we can show, uh, the claim button for the tokens. If there is no account, then you shouldn't be showing this button right over here."
So again, we're going to show a, uh, button, and then we'll be using the, be using the, uh, `Button` component here, um, from from our shadcn-ui and what we're going to do here is we're going to create first, um, a state variable for `isClaiming`, and we'll set this, uh, `useState` the default to `false` here. And then what we'll do for the button here is, uh, depending on the state of the button here, depending on if the button is claiming or not, we'll show some "claiming" text with a loader, which we can, uh, import from `react-loader-spinner` here. And then if it is not claiming, the text should show "Claim Button" here.
Next thing we'll do is create the, uh, API call and function for claiming the token, uh, using third web's Engine. What I'm going to do, coming back here, uh, and let's just test this out really quick, right? If I use my Passkey to sign in, I should see a button appear. Once I have my account, there you go. We have "Claim Button" or "Claim Tokens." And now I should be able to click on that. We didn't add any functionality, but we'll add the functionality here for it to then set, um, the claiming state, the claiming state, and then send us the tokens here to our account.
Now, coming back to the third web dashboard here, again, we are going to be using third web's Engine to implement this button. And this is just to go over and showcase some of the tools that third web has to offer. You don't have to necessarily build this into your application. So if you want to skip on over to the next section, uh, you can check out the timestamps below. But if you want to check out and see how cool and amazing it is to implement and how easy it is to implement tools like Engine to interact with the blockchain, uh, you can follow along here and we can create this button to, uh, allow our users to be able to claim some tokens here.
So coming back to the third web dashboard, we'll come to the "Engines" tab here. And this is where you can set up your Engine instance, or if you already have one, you can select and see all the details of your Engine instance here. Engine, in a nutshell, if you're not familiar with it, has these backend wallets here. And these backend wallets, we can basically utilize them and make API calls to Engine to specify these backend wallets to execute certain on-chain transactions for us. So right over here, I have a backend wallet, uh, which I'm going to copy the address for, um, that we can make a call to and say, "Hey, mint these ERC20 tokens to this wallet address." In this case, mint the prediction token, uh, token to the user's wallet address that I specify. And with that, we can now mint tokens from our contract, uh, and a user doesn't need to execute any transactions or anything. They just simply press a button, and we can then execute and tell this backend wallet to do all the on-chain execution for us.
Now, the reason I copied this wallet address is right now on our prediction token or ERC20 token that we created, only the deployer has the permission to mint tokens and distribute tokens and everything. So we're going to come to this "Permissions" tab here in our token contract and we're going to come to the "Minter and Creator" here, and we're going to make sure that our backend wallet of Engine has the ability and permission to mint and create tokens as well, because again, that wallet is the one that's going to be executing all of the transactions on chain for us. So we're going to update our permissions here. And once we have that updated, we are set to go.
And now we can implement Engine into our application. And when I'm going to do here is in our `app` folder, I'm going to create a new folder here called `api`. We're going to create a new folder in that, uh, call it `claim-token`, and then we'll create a new file in there called `route.ts`. And the first thing we're going to do in this `route.ts` file is make sure we have some environment variables to execute our calls to Engine. So one, we're going to need to specify the backend wallet address that we are, um, telling to execute these transactions on chain, which is the wallet address that we just copied and gave permission to. We're going to get our Engine URL, and then we're going to get our Engine, um, or third web secret key, which is our Engine access token. And those two things are going to give us the ability to interact with the backend wallet and tell it what to do. In this case, to mint tokens to the specified wallet address.
Now, I am going to add a couple functions here that I'm not going to go too deep into. Um, we do have a bunch of other videos explaining how Engine works and everything. In a nutshell, what these functions are going to do is check the transaction status. So when you execute a transaction through Engine, it's going to give you a QID of the transaction, and that transaction will have different statuses like "queued," "sent," or "mined." Basically, if the transaction is being queued up, uh, if it's been sent on chain, and then finally, if it's been actually mined on chain. So with that, we can check the transaction status based off of the QID that is returned off of our API call to Engine. And then we just have a `pollTransactionStatus` here that checks that status every so often. And then once we get back the "mined" status, we know that the transaction on chain has been, uh, has been mined. So that's when we know for sure that the, the, the transaction has been executed. And then we'll return, uh, that status back to our user so we know whether or not the token is still being sent to them or not.
And then we are going to create our `POST` function here, which is going to take our request. We're going to check to make sure that we have our environment variables here before executing. We are going to get the address from our request body here. That address, again, is the address of our user. So when we make our call to Engine, we're going to provide the, we're going to provide the user's address so we know who to send the ERC20 tokens to. Then we're going to create our response here, which we're going to make an API call to Engine. So again, we're going to provide this endpoint of our Engine URL, our contract, the chain ID that we are executing this on, the token contract address, we're specifying that it is an ERC20, and that we're going to call the `mintTo` function. Again, this is going to be a `POST` method. It's going to take our authorization, which is going to be our third web Engine secret key or access token. We have to specify the backend wallet that we're calling this to execute the transaction for us. And then we have to provide the body here, which is going to be the `to` address, which is going to be the address of the user that we provide, and then the amount of tokens that we want to, uh, mint and send to them. In this case, we're going to just say 100 tokens, but of course, you can just play with this amount, uh, to your liking. And then if the response is okay, again, we're going to poll our transaction here with the QID and wait for us to get back the "mined" status, which means that we, uh, executed the transaction on chain and that the tokens have been sent. Course. And then if it's been mined here, we'll say the transaction has been mined successfully. If it doesn't return "mined" within the time period that we sent, uh, we just say it's not returned that it wasn't mined within that certain, uh, time period.
So with that, we can come back to our `Navbar` here, and then we can create our function here for claiming our tokens. So right here, we're going to have our `handleClaimTokens` function. It's going to be an async function. We'll first set the `isClaiming` state here to `true`, and then we'll run a try catch here. We'll make our call to our API `claim-token`. Again, it is going to be a `POST` method, and in our body here, we're going to provide it the address, which is going to, we can get the address from our `account.address`, so we can get the connected account here and pull the wallet address from it and provide that as the address we need to send the tokens to. And then if the response is not okay, then we'll just throw an error saying that it failed to throw or failed to claim the tokens. And then finally, if everything goes through, uh, we can set the `isClaiming` here back to `false`, and we know that the tokens have been claimed.
So on here, on our button, uh, we'll set the `onClick` to `handleClaimTokens` function. We'll set `disabled` here to `isClaiming`, and then we'll set the `variant` here to `outline`, uh, for our button. And with that, we can come back here to our demo, and we now have this "Claim Tokens" button here. So now if I hit this "Claim Tokens" button, it's actually not going to go through just yet because we didn't set our environment variables for our Engine here. So again, you'll set these environment variables. You can store this in your `.env` file. I'm going to set that up right now and then come back.
All right, so I've set up these variables here, and I'll quickly go over where you can get that information. So if you are again using Engine, you'll click into your Engine instance. Here's going to be the list of your backend wallets. So that's the address of the backend wallet that you are specifying is going to execute the on-chain transaction for you. Up here is where you're going to get your Engine URL, so you can just copy that there. And on the side here, if you go to the "Access Tokens," this is where you can create an access token, and then you'll provide that access token as your third web Engine access token. And with that, we'll come back to our app here, and we should now be able to claim tokens here.
So we can hit the "Claim Token" button. You can see it is claiming these tokens. It's putting through our transaction. We can actually check Engine here really quick, right over here, and you can see, um, our transaction has already been mined. They went through 8 seconds ago. And then we can come back here, you can see the "Claim Tokens" is no longer loading. And if we look now, we should have, there you go, 120 PMT tokens, or prediction market tokens. That was a really quick implementation and use case of how you can use Engine. Again, a user doesn't need to sign transactions or anything. We're just making an API call to Engine to execute the minting and transferring of these tokens to our user wallet address. And again, this is just for a demo use case in our application because we're going to need these tokens in order to purchase shares in our actual prediction market.
Right, and then the next thing we're going to do is build out, uh, our tabs here for our different prediction, uh, markets depending on the status of them. And of course, you can add your, um, you can add a banner, you can add whatever you want here. We just put a placeholder image here just to kind of give it more of a feel and everything of an application. Uh, but we're not going to go over how to add that. We're just going to go over next on implementing the tab section here, and then how to build these market cards and pull in that information and display it in your application.
So what we'll do is we'll head over to the prediction markets dashboard here. And like I said, the first thing we're going to do is be able to display the prediction markets, or the markets in our application. Now, before we do that, we want to know what prediction markets and how much prediction markets we should be showing. So what we can do is get the prediction market count. And we can get the count by using the `useReadContract` hook from third web. And we can provide this with some information like our contract, which is going to be our prediction market contract that we stored in our `contracts.ts` file. We can then provide it the method of the function we're going to. We can then provide it the method that we're going to be calling from the contract. Uh, in this case, we are going to call the `marketCount` function. So we're going to call the function `marketCount`, which is a view function and returns a `uint256` here. And then we're going to provide it any params for that function, which in this case, it doesn't take any parameters. And while we get back is a data, which we can rename here to `marketCount`, and we also get the `isLoading` state of it, uh, which we can set, uh, as `isLoadingMarketCount`. And now we should get back the market count, so we know how much markets are within our prediction market contract. And we can then, of course, go through and map that array of markets and we can display information cards for each one of those.
So with the `marketCount` here, under our navbar, we're going to display our tabs here. So we're going to import the `Tabs` component from the shadcn-ui components we imported earlier. So we'll import all of these. And then the three tabs we have are "Active," "Pending Resolution," and "Resolved." Right? So now if we come back to our demo app, uh, oh, uh, we did it again. We'll make sure we have our prediction market dashboard as `useClient` here. Refresh that one more time. There you go. So we have our navbar, and then we have our three different tabs right over here. Now, depending on the tab, we're going to display, um, the cards that belong into each one of those tabs. So below this here, we're going to check, um, `isLoadingMarketCount`. And if our `marketCount` uh, is loading, we're going to show `TabContent` here, which will import from our shadcn-ui component. And we're going to create a component called `SkeletonCard` that just shows a skeleton loading card while it loads. And then that is if the, we're still loading the market count. And once we have the market count, what we can do is we can then rate the contents of each one of those tabs. So the `TabContent`, if the value is "active," we're going to show a grid here. The grid is going to be an array with the length of the `marketCount`. And what we're going to do is depending on what the `marketCount` is, we're going to map through that array and we're going to create, uh, the card for an active, uh, market. And same thing goes for "pending" and "resolved" here. So again, we're just getting the array based off the length of the number of markets and then we're mapping through each one and then we're going to display the card components here.
What we'll do next is we'll create that `SkeletonCard` component here. So we'll just come back here and then we'll create a new component here and then we'll just call this new file and then we'll call this our, um, `SkeletonCard.tsx`. And for our `SkeletonCard`, we're just going to use the `Card` component. We're going to show, uh, and with this, uh, skeleton here, it's going to show a `Card` component and then we're going to have, uh, it animate with the pulse animation. And we're going to just show some like gray placeholders, uh, for like the title and the data of the card here. And then in here, we'll just come back to `SkeletonCard` and we'll make sure we change this to `MarketSkeleton`. And then we'll come back to here and then we'll create here a, um, variable called `skeletonCards`. Uh, this is going to be from our array here. We'll set this here, not the `marketCount`, we'll just set it to six as a default. And then in here, we'll just say, um, for each one, we'll give a `SkeletonMarketCard` or a `MarketCardSkeleton`. And then we'll provide a key, the, uh, index here. And then we'll save that.
Yeah, if we come back to our application here and I refresh, there you go. You can see we had a really quick load of our skeleton cards. Don't mind the errors, it's because we haven't set up the card components for each one of these tabs yet. So now that we know that it's going to show our skeleton cards, let's come back here and the next thing we need to do is again, these are where the errors are coming from. Let's create the card components that should be displayed for each one of these. So in our application here, in our `components` folder, we're going to create a new file and we're going to call this our `MarketCard.tsx`.
First thing we'll do here is create a few interfaces for our `MarketCardProps`, our `Market`, and our `ShareBalances`. So for our `MarketCardProps`, we need to know the index of our market card and the filter that it should go under. Is it going to be "active," "pending," or "resolved"? And then the interface for our `Market` here, this is going to be for the data that we're going to pull for that specific market based on the market ID. Again, the question, the options, end time, outcome, total shares, and if it is resolved. And then finally, we have the interface for our `ShareBalances`. So this is going to be the balance for a user based on option A or option B shares.
Next, what we'll do is create our `MarketCard` component here. And then we're going to take in the index, so we know what market we're getting the information for, along with the filter that that market should have. Again, this is coming from our `MarketCardProps`. So similar to how we got the `marketCount` for the, um, prediction market dashboard to know how many markets we're going to be displaying, we're going to do the same thing here using the `useReadContract` hook. We're calling this on our prediction market contract that we stored in our `contracts.ts` file. Method that we're going to be calling is the `getMarket` function, which takes a market ID and it is a view function that returns basic, that returns the market struct here, right? Which is basically going to be this here of our question, or options, and time, total shares, if it's resolved or not. And then what we're going to do in the parameters here is provide the index that is provided to us, uh, which we're going to make sure is a `BigInt` by using `BigInt` here. And then we're going to parse the market data. So we're going to create a variable here called `market`, which is of type `Market`, uh, and then we're going to take the market data that we get back here and set all the data here, um, according to the question, option A, and everything here, uh, that way we can easily use that within our card here to pass the data through.
Really quick, at the top here, we're going to get our `account` variable here using the `useActiveAccount` hook. Again, this is going to get us things like the, uh, information of the account connected to our application here. And we'll need that because the next thing we're going to do is to get the share balance, uh, for the user here. And the share balance, again, using the `useReadContract` hook on our prediction market contract, we're going to call the `getShareBalance` function, which is going to return us back the balance of option A and option B shares that the user has. So we have to provide it what market index we're looking for this, and the user's account, uh, or wallet address here is what we're going to provide as the parameters. That's going to give us back the shares, and then we're going to parse through the data and set that to a `sharesBalance` variable, which is of the type `ShareBalance` here, um, which is going to give us the option A and option B shares here.
Next, we're going to check if the market is expired or not. So we'll create a variable here called `isExpired`, and it's going to take the time, the end time of the market, `endTime` here, and then check it against the current date time. And we're also going to check if the market is resolved, which we're going to set `isResolved` by checking the `resolved` value here on our market. Next, we are going to create a function here called `shouldShow`. And what this is going to do is based off if our market is expired, if it's been resolved, we're going to set set the filter here for the specific card. This card will show up in the correct tab, uh, on our marketplace dashboard. So if there is no market, we're going to just return `false` here. But we're going to use a switch case here for the filter, and we're going to set the filter to "active" if it is not expired. If it is expired and it hasn't been resolved, we'll set it to "pending." And if it is expired and it is resolved, then we should set the case here, or the filter to "resolved." And then default, we'll return `true` here. And then we'll just check here if, if the market should not be shown at all, then we'll just return `null` for the market card here.
All right, so for our `Card` component here, we're going to use the `Card` component from shadcn-ui. We'll set the key here to the index. And then if the market data is still loading, we're going to show a `CardSkeleton` here. And then once we have all the market data, once we have gotten all the market data, we show the `CardHeader` here, which we're going to show the question along with the expiration date. We're going to show the contents of the card, and depending if the card or the market is in progress, or if it's been resolved or pending, uh, we're going to show the proper data there on top of our card. So we're going to create those components for that there. And then finally, down at the bottom, we're going to show the market shares, uh, what their winnings are of the user, what the current holdings of the user are, and everything.
So now if we take this card and we come back to our `PredictionMarketDashboard`, in each one of those arrays here, we're going to provide this with our `MarketCard`, and we're going to say, "Hey, this should have the filter of 'active'." This one should have a filter of "pending," and then this one down here should have the filter of "resolved." Uh, we'll also make sure to add keys for each one of these. So we just say `key`, we'll just make it the index. There we go. And now if we come back here to our application. So now when we come back here, you can see there you go. We have our one card with the one piece of data that we're showing here, and that was our market question of "Will Bitcoin hit 1K tomorrow?" So now what we're going to do is build out the other components of our card. If we come back here, that's going to be like the expiration date, the progress bar here, and ability for our users to now select and purchase, uh, these shares using the predict tokens.
So we'll add the expiration first here. And what we'll do here is come back to our `Card` component. So we'll come back here, we'll come to our `components`. We're going to come back to our `MarketCard`. We're going to add in the expiration date first. So we're just come into `components` and create a new file. We'll call this our `MarketTime.tsx`. And in our `MarketTime` component here, we'll create an interface here for our `MarketTime`, which is just going to be the `endTime` right over here. And then we're going to then format that time. So depending, so we'll take that date time string and then we'll format it into year, month, and day here. And then for our component here for our `MarketTime`, we'll say if it is "ended," uh, we'll check to see if it has by comparing the `endTime` date with the current date. And then we'll format the date as well. And then for our, and then for our component here, we have our default class values here. And then if it is "ended," we are going to set the border here and everything in the text to red. If it's not, then we will set it to gray, which means that it hasn't expired yet, but we'll change it to red if it has expired. And then if it is "ended," we'll say "Ended at" the date. And then if not, we'll say "Ends" at the date that is formatted.
So coming back to our `MarketCard` here, above the title, we can add our `MarketTime` here, which we can import. And then the `endTime` here is going to be our `market.endTime`, which we will provide it right over here. So if we take a look at now our application, we should see our end time here, which in this case is going to be, um, one day from me recording this video, which is November 19th. We're also going to come back here and then, uh, for the `SkeletonCard` here, while the data is loading, we will make sure that we provide this with, uh, our `MarketCardSkeleton` component. So we save that. We'll come back here. If we refresh our page here, you should see our market card skeletons. Um, and then depending if we get the data or not, we should be able to now display our markets here. So that is working.
The next thing we need to do is, and then the next section of the card that we're going to be building is this progress section here, which is going to show the results of the current market. So we'll show the current shares for each market and the percentage of those shares according to the market here. So we're going to work on that. We're going to call that the "Progress Area," and we're going to come back to our application here. So I'm going to come over to our `components`. We're going to create a new file. I'm going to call this
Our Market progress TSX and in here we're going to create an interface for our Market progress props which is going to take the option A and option B whatever the ring of those options are and then our total shares as well. So we need to know the total shares of option A and the total shares of option B.
So in our component here, we're going to call it Market progress. We're going to pass those things on here, which is going to be the text for option A and B and the total shares. We're going to create the total shares, which we are going to add the number of total option A shares and total option B shares to get the total shares. And this is going to allow us to to calculate the yes percentage. As long as the total shares is greater than zero, we're going to take the total option shares of A, divide it by our total shares, multiply that by 100. That should give us the percentage of option A shares. And if there are no shares by default, we're going to show 50/50 for each of the options. Uh, and with one percentage of one option, we should be able to calculate the percentage of the other option.
So in our component here, we're going to just create a div here. And what we're going to show on one end of our div here is going to be option A, and then we're going to show the total number of shares here. We're going to make sure that we round this to the nearest whole number here. So we're going to take the total shares which gets returned to us in way. So we're going to use the two ether utils function here from third web to create that into an ether value. Then we're going to use the par in here to make sure we have a number that we can use the math floor on. So we show option A, the name of option A, and the total number here, and then the share percentage. Again, if the share is equal or not greater than zero, it's going to not show anything. Uh, but if it is greater than zero, we're going to just show the yes percentage here. Uh, we'll make it a round off that percentage here to a whole number. So we'll use the math floor of the yes percentage. Uh, then we'll do the opposite for option B, right? We'll get option B, we'll display the total number of shares for the percentage here, though. We're going to take 100 minus the yes percentage, which will give us the opposite. Uh, and then give us the total percentage for the option B here. Then we'll finally display a progress bar here. Uh, this progress bar will be a UI component that we're getting from Shad CN, and then the value is going to be the yes percentage here.
So we can come back to our Market card here, and then for our Market progress component here, we will add in our Market progress. Now we're going to provide it with our option A, which is going to be our Market option A, our total shares, which is are going to be our total shares, and everything as well. So again, we're getting all that market data up here based off of the index or the market ID, and that is what we're going to provide here into our Market progress bar. So if we save that and come back to our example here, what we should see is a 50/50 split, right? Because we haven't, uh, no one's purchased yes shares, no one's purchased no shares yet. So we shouldn't see, um, anything as far as the progress bar leading one way or another. So it's going to stay here at 50/50 until we start buying shares.
That's going to bring us to the actual purchasing and buying interface here, uh, for our user. So again, when we, we should show our two options. When a user selects an option, they can then buy shares for that option, right? So if I select Dolphins here, it should show me Dolphins. If I select Raiders, it should show me Raiders. We have an input where a user can now put in the amount that they want to purchase. When they hit confirm, we first have to, if you remember, we are using a custom ERC20 token. So they first need to approve the allowance of that token to the contract, and then they can confirm the transaction. Uh, we're going to make it two separate transactions here, just so we can walk through the flow, but again, we are using an abstraction as well. So if you wanted to, you could batch those transactions together in one transaction too.
So we're going to build that buyer interface here. So I'm going to come to my components, create a new file, and I'm going to call this our Market buy interface. TSX. We're first going to create an interface here for our buy, our Market buy interface props, uh, which is going to take the market ID, cuz we're going to need to know what market the user is buying shares for. We're going to need to know the market itself. We're going to provide it the market itself and provide it things like the question, along with the strings for option A and option B. And then we are going to create types for buying steps. So again, we have the initial step where the user is selecting their amount and everything that they want to purchase. Then they have the allowance step, which is where they have to approve the allowance of the token. And then they have the confirmation step, which is them confirming the actual purchasing of the shares and calling the function on on our contract. And then we have options here where we have if they should show, uh, if we should show option A, option B, or if we should show no here.
So we'll create our function here for our Market buy interface, again taking in our market ID and market from our Market buy interface props. And then the first thing we're going to do is we're going to get the data of our user again. So we're going to use the use active account hook. We're also going to use the use send and confirm transaction hook. This is going to allow us to send transactions on chain and create a function for us to execute transactions. Then we're going to create some state variables for our UI state, so whether our user is buying or not, if we should set the visibility of our components, the container height to adjust for our component to be dynamic within our card. And then we are going to use, um, we have a content ref here, which is going to use the ref so we can get the height of the container and dynamically adjust it. So we're going to import use ref here and use state. And then we have a few more state variables here for managing the transaction state, so our selected option, the amount that the user is going to be sending, which by default, they're going to be on by default. Again, we're sending this to the initial state, is approving and is confirming here. And finally, we have a state here for our error, where we can set our error, um, if we run into any.
Next, we're going to create a use effect here that is going to adjust the height of the container where our component is going to be. Again, we're going to take our reference to our content reference here of the current container, and depending on the height of our container, uh, when we initiate certain actions or steps within our flow here, we can adjust the height here. So this is just to again update the container height, uh, when we change through the different states of our component.
Next, we're going to create two functions here for one handling the buy here. So what we're going to do is the options are going to be either option A or B, and depending on which option the user selects, uh, we'll know which option the user is buying the shares for. So once, uh, when they click on option A or B, we set that the visibility to our initial selection area to false, and then we set is buying to true. Uh, we set the selected option to whichever option they selected, and then we set the visibility to true here. And then we have a set timeout here because we added some nice animations. We're going to be adding some nice animations and everything to our UI. So this is just to match the transition duration here, so that we see that nice smooth transition. And then when we handle the cancel here, we set the visibility to false. We are going to then set is buying to false. We set the buying step back to initial. So anytime someone hits cancel, we'll select set the selected option back to null, the amount back to zero, we set the error back to null, and set the visibility back to true. So again, this is just whenever someone hits cancel, it just resets everything back to the default.
Next, we're going to create a function to check if our user needs to approve token spending, right? We need to make sure that because we are using a custom ERC20 token, that the user has given approval for transferring and spending of the token to our prediction market contract, because that contract is then going to be transferring those tokens out of the user's wallet, uh, because they're using that as a payment. So we're going to check first if the the amount has to be, uh, greater than zero that they're entering inside of our the amount that they want to buy for shares. If it's not greater than zero, we're going to set the error to "Amount must be greater than zero," so we can display that to them. Uh, if not, we set the error to null. And here in our try catch, we're first going to get the user's allowance. So we can use the rec contract function here, where we're going to provide the contract as the token contract. We're going to call on the method of the allowance method. So we're going to check the allowance of the owner's address and the spender address, which is going to be our prediction market smart contract. And we should then get a return value of a un256 of what that allowance is. And again, the parameters that we have to provide here are going to be the address of the user and our token contract address, and then the spender should be our prediction market contract address here. So then we're going to set the buying step here depending on what the user's allowance is. So we can take the amount here of the user's allowance. So we can take the amount that the user entered, right? We can turn that to a string and then use the two-way utility tool here from third web. What this is going to do is convert the basically ether amount to a way amount, which is what we get returned back when we check the allowance here, um, on the token contract. So we want to make sure we compare the two-way amounts. So make sure we convert that two-way and we convert that to a big int. And if the user's allowance is less than the amount that they want to spend, we have to make sure we prompt them to confirm and, uh, that allowance and confirm the token spending there. If they have an allowance that is greater than the amount that they are putting in, then we can just send them directly to the confirmed transaction because they don't need to set any more allowance on top of that. And then we'll just have a catch here for any errors that we have, and we'll set that there.
And then the next function we're going to create is the handling of the setting of approval. This is going to be an async function. We're going to set is approving to true so we can show a loading state in our UI. We're then going to create a transaction here, which we're going to await, and we're going to use the approve extension from third web. Since we're using an ERC20 or pre-built ERC20 contract from third web, we actually have these extensions pre-built for things like ERC20, 721, 1155, and other token standards. So this will actually just call the approved function from the contract itself. We can import that from third web extensions ERC20 here. And again, what this is going to do is we're going to create a transaction to set the approval. Uh, we're going to call this on our token contract. So we have to set the approval for our token on our token contract. We're setting that the spender is going to be our prediction market contract address. So our prediction market contract is the one that we're allowing to spend these tokens for us. And then we're setting the amount to whatever the amount is that the user inputed, uh, that they want to purchase for shares. And then from there, we're going to await and we're going to take the mutate transaction here, which is what we did with the use send confirm transaction. We're going to mutate the async and get the mutate transaction here. This is going to allow us to again execute, send the transaction, and confirm it. Uh, so we're going to use that here, provide it with our transaction, and then we are going to then set the buying step here to confirm. And then finally, we'll set is approving to false.
And then finally, we're going to create our handle confirmation. So this is going to be where we execute and call the buy shares function from our prediction market smart contract. So we're going to make sure here we have our handle confirm function. It's going to be an async function. We're going to check to make sure if there is no selected option or our amount is less than or equal to zero, we're going to set the error to, uh, the user has to have a selected option and has to have entered an amount greater than zero. If they have, we'll set is confirming to true. We're going to now then use the prepare contract call function here from third web's SDK. We're then going to create our, uh, transaction here. We're going to prepare a contract call. Uh, we're again using the contract, the contract, the function that we're calling is on the prediction market contract, and we're going to be calling the buy shares function here. We're going to have to provide it with the market ID, if they are selecting option A or option B, and then the amount of shares that they want to buy. So here the parameters that we need to pass are a big int of the market ID. We have to say if the selected option is equal to A or not, right? Cuz if it's not, then we know that they're buying option B. And then we are going to get the amount. Again, we're converting that to a way value because we need to provide this to our smart contract, which we need to provide it in way, and then we're going to convert that to a big int here. Then we again, we'll await, uh, and send and confirm our transaction using the mutate transaction action here. And then we will call the handle, uh, cancel function that will reset everything back to default and bring everything back. And then finally here, we will set is confirming to false.
And then for the component itself, we are going to create a container here. Now the container's height is going to again be dependent on what current state we're in. So we are going to set the height here to the container height, which we again getting from up here, the container height. And as we use the use effect to check what the container height should be, we set that height here dynamically. And depending on the state, um, of the flow that the user is in, if they are not buying here, we're going to show two buttons for our options here, right? We're going to show a button for option A and a button for option B. And when a user clicks on one of those buttons, we call our handle buy and provide it which option they selected. Then if they are in the buying step, we're going to make sure that, uh, we can set the allowance here. So we're going to make sure that if they need to set allowance here, we are going to create a section here that says they need to set approval and they need to, uh, approve the transaction before proceeding. So here they, uh, we're going to provide a button here. One of the buttons on click is going to handle the set approval function, and we're going to make sure that it's disabled when it is approving. As it's approving, we're going to show a loader with approving text, and then that button should say set approval. Uh, the other button here, we'll have on the side, is going to be a cancel button, so they can just hit cancel and reset everything.
Now, if they are in the confirmation step, we're going to show something very similar where they can confirm the transaction. We'll say "You are about to buy," and we can display them the amount that they're about to buy for whatever selected share they have. And then they have a button here where on click it will handle the confirmation function, calling the buy share function on our prediction market contract. And if it is confirming and loading still and executing the transaction, then we will just show a confirming with a loader. And then if not, we show the confirm text and button. And then again, we're going to display a cancel button as well, right over here in the input section. This is where the user is going to be able to input the amount that they want to purchase. We'll have some text here saying that one of market, um, one of option A is equal to one predict token, or in this case, a prediction market token, so PMT, and then one of option B is equal to the same as well. Our input here is going to be a type of number. We're using the input here from Shad CN components as well. So the type is going to be number, the minimum is going to be set to zero, the step increments are going to be by one, placeholder will say "Enter the amount." The value here is going to be set to amount. On change, we will basically set the amount or change the set amount value to the value of whatever the user has in here. We just have some styling here where if there is an error, we'll make sure the input is highlighted in red. Next to that input, we'll show what share or what option the user is purchasing, whether it's option A or B. And then if there is an error, we'll display that error below the input field. If they have an error, like they didn't input something greater than zero, or, um, whatever error may occur, uh, we display that error if there is any. And then we have two buttons here to confirm or to handle cancel. Right, cancel is just going to cancel and reset everything. Confirm is going to run the check approval function, and that's going to tell us whether we should send them to the approval step or the confirmation step, right, based on their approval of the token or spending for allowing the prediction market contract to spend the ERC20 token on their behalf.
So there is that component. We'll come back to our Market card component. We'll add this in to our buy interface here. We provided our market ID, which is going to be the index, and then the market here, which is going to be the market, right, the data of the market that we're pulling. We're just going to pass that data on to the market buy interface. So now if I come back to our component here, we'll load this and log in. There you go. Once we're logged in, we now have our two options, yes or no. So if I hit yes, we should say "1 yes equals 1 PMT token." I can input how much yes, uh, shares I want to buy. Same thing goes for no. I can put in how much no shares I want to buy. So will Bitcoin hit 1K tomorrow? Let's say yes, it will. And let's buy five shares here. So now if I hit confirm, it's going to check if I need to set approval, which I do, because I haven't set approval for any tokens yet, uh, for this prediction market contract. So approval is needed. So we'll hit set approval. Again, this is going to call the approve function on the ERC20 smart contract, setting my approval to the five tokens transfer. You can see here it went through. So now I can confirm the transaction here, and we are going to buy five yes shares. So I can hit confirm now. This is going to be calling the buy shares function on the prediction market smart contract that we just deployed earlier. There you go. You can see we now have five shares here, and then we have no set at zero. So you can see this is 100% yes right now.
I can actually come back here, let's disconnect this, and let's sign in with a different account here. So we have a different account. We need some tokens. So we're going to claim some tokens here. So once we have our tokens claimed, you can see here we have 100 PMT tokens in this account. We're just going to purchase no, and and then we'll hit five on this one as well. So we'll hit confirm. We'll set approval again. We're using in-app wallets. We're using a con abstraction here. There are no really web3 popups or user interfaces that a user may not be familiar with that we're going to be prompting them with. No gas transactions, none of that, because we're utilizing things like account abstraction, uh, to sponsor gas for them, and we are using things like engine for them to be able to easily claim tokens. You can see here now it's five and five, so it's setting back at 50.
So the last and final thing here, if we take a look back at our components here, is just showing the user's current shares and the winning results based on if they have the winning result or not. What's going to be shown. So we'll come back here to our components. The next component we'll do is displaying the market shares of our user and like how much shares they have of each, uh, or each market, how much shares of which options they own. So create a new component here, a new file in our components folder. We'll call this our Market shares. TSX. And we're going to create a two fixed uh function here really quick. So we're going to come to our lib folder, we're going to go to our utils here, and we're going to create a function here and add one called to fix, which just formats our numbers to a specific decimal place.
We'll come back to our market shares here, and the first thing we'll do here is create an interface for our market shares display props. This is going to take in the market data here, like the options and the total options, and the share balance for our specific user shares. So in our function here, we have our market shares display fun, uh, component. We're going to create a state variable here for our winnings, where we can set the winnings. Again, this is going to be, um, different values for option A and option B. We're going to create a function here to calculate the winnings. So depending if it is option A or option B, we'll check if there is a shares balance. Um, if not, we will return zero. And what we'll do is we'll get the user shares. So if they have option A, then we'll take in their option A shares. If it's option B, we'll take in their option B shares. We're going to get the total shares for that specific option. So we'll get the total shares of option A and total shares of option B. And then we'll get the total losing shares as well of option A and option B. So depending on what we're calculating the winnings for, um, we'll set those there. If the total shares equals zero, then we just return zero. We don't need to do any more calculations here. But if it is not zero, then we need to calculate the user's portions of the winning. So user's portions is going to equal the user's shares, and we're going to divide that by the total shares for option. If they're on the winning side, if they're on the losing side, we'll do the total losing shares times the user proportions here. And the total winnings is their original shares plus their portion of the losing shares. And that will basically calculate what the user is winning, uh, if they have the winning result here.
We'll have a use effect where when we get the share balance and everything, uh, will calculate the winnings for option A and option B based on the user. Then we'll set the, then we'll create variables for the display values. So we'll use the twoofixed function that we just created in the utils in our utils.ts file, and we'll then display this to a fixed value of two decimals here. Then finally, we're going to display this. We'll say "Your shares" based on market, option A, and Market option B. We'll show those shares here on the total shares that they have. So we can display the share balance. We'll say "two ether" cuz they're going to get returned to us in wave values. We'll use parse int and then math floor to just get a whole number for us to display. And then here, depending if the winnings are for A or for B, we'll use the values of that we calculated and the display values here and display them, uh, using the badge components from Shad Cen.
Now I come to the market card here, uh, market share components. We're going to add in the market share display components right over here, and we are going to provide this with, we'll provide that with the market data and the share balance data here. So coming back here, we'll come back to our demo application, and we will now see that our shares here for our account here, we have five shares of N. If we do win on the N side, we will receive 10 shares, all of our five shares plus the five shares of the yes here.
And then finally, what we're going to do is what if a market is all resolved and we can claim our winnings? We're going to come here, we're going to create a last component here. So we're going to say, um, this is going to be our Market resolved component. TSX, right? So if our market is resolved, we should be able to claim winnings if we have winnings available. So for this component here, instead of showing the buy interface, we're going to show a claim reward interface. We'll show our claim reward component on the card instead of the buying flow that we created earlier.
So for interface of our Market resolve props here, we're going to take the market ID, the outcome, and option A and B. Then we're going to create our Market resolve component here. We're going to get the user's account here, um, because we're going to need to know what rewards they can claim if they have any. And then we're going to have again the ability to send and confirm a transaction, which we're going to call the claim winnings function. Finally, we have a state here to check if the user is claiming their rewards or not. We're going to get the share balances here of a specific user, so we know what they can claim. So we're going to use the use contract hook again on our prediction market contract, calling the get shares balance, providing it the market ID and the user's wallet address. Then we'll create a function here called handle claim rewards, which is going to be an async function. We'll first start by setting the is claiming to true. We'll create our transaction here by using the prepare contract call, and then we're going to call on our prediction market contract the claim winnings function here, where we have to provide it the market ID that we're claiming the winnings for. So we provide it the market ID, and then we'll execute that transaction here by using the mutate transaction using the use send and confirm transaction care. Finally, we'll set that the is claiming to false.
So right over here, we're going to check, uh, if the market is resolved and what it has been resolved to. So we'll say resolved, and then we'll show which option was the resolved option. We'll then show the claim button. Uh, if there is a shares balance and a user has a connected wallet, we'll display them a button, uh, to claim those rewards. So only if they have a balance will they actually see a button there. And then we'll have this button called the handle claim rewards function. And if it's claiming, then it'll just show a claiming, uh, text and loader. And then if not, it will show the default, uh, claim rewards text here. And that is what it will show for an un a resolved market for a user to then collect their rewards.
So what we can do is in our Market card here, we can then just put in our Market resolved component. Uh, this is going to take our Market ID, the outcome, and then our option A and option B here. So now, once a market is resolved, what we should see is a resolved market like this, and the ability for a user to claim their rewards. Again, if they already have claimed rewards or don't have rewards to claim, they're not going to see their winning results here, um, and then they won't be able to claim anything because they won't have any winnings or rewards to claim.
And there you have it. We deployed our very own and created our very own simple prediction market web3 application. Again, we went over what a prediction market is. We went over some of the use cases that prediction markets are used, like the use case that Poly Market has been able to use it to predict the 2024 US presidential election. We then went through how to build and create and deploy our very own simple prediction market smart contract, and then we used the third web and built our very own web3 application and built our very own front end for our simple prediction market to interact with the smart contract. We created a user can then a user can now claim tokens and then use those tokens to buy shares in a certain market in our prediction market. Depending on the shares that the user has purchased and how the market resolves, a user can then claim their rewards.
So I hope you folks learned a lot in this video. If you need a reference back to any of the information in this video, all the timestamps and information that we mention are in the description down below. Again, I hope you folks enjoyed this video, you found some value in it. If you did, give this video a thumbs up, hit that subscribe button, and don't forget to turn on the notification bell so you don't miss out on more videos just like this. Again, I hope you folks enjoyed this video. Can't wait to see how you folks implement prediction markets into your own web3 applications, and until next time, see you.