We need to give the owner of the smart contract the ability to open and close an NFT sale in the React dApp UI.
We’ll use the .openSale
and .closeSale
methods off of the connectedContract
stored in React state. Then for the best possible experience we’ll create a pending state while the sale is opening or closing. During the pending state the buttons will be disabled.
Then we’ll bring in useToast
from chakra-ui to give a message depending on if the opening/closing of the sale was successful or not.
Instructor: [0:00] Next, let's wire up the Open Sale and Close Sale button in our app's admin panel. First, we'll go down below to our route components, and here's admin. We're going to pass in two props, isOwner and connected contract. Next, we'll go into the admin component.
[0:19] We'll update our incoming props, isOwner and connected contract. Below we have two buttons, which we can disable if someone's not the owner, just in case a non-admin were to access this page. I can say, isDisabled equals not isOwner.
[0:38] Copy and paste that, and do the same for the Close Sale button. Then we'll get the Open Sale button and onClick callback, onClick equals open sale. We'll come above to find a new asynchronous function, const open sale equals async. Inside the function, we'll add a try block, try catch error. If we receive an error, we'll just log it out for now, console.log error.
[1:06] Inside the try, we'll check to make sure we have a connected contract before doing any more work. If not connected contract, then return. Below, we'll create a new variable. Let open sale transaction equals await connected contract.open sale.
[1:27] Below, we'll add another await statement to wait for the transaction to resolve, await open sale transaction.wait. Then we'll go to the top of the file and import useState from React. Import useState from React.
[1:45] Come down below and say, const open sale transaction pending, set open sale transaction pending. We'll set it to useState false. Then inside of our openSale function, we'll set open sale transaction pending to true.
[2:06] When the transaction resolves, we'll copy and paste this and set it back to false. If an error is thrown and we end up in the catch block below, let's just set it back to false as well.
[2:15] If we go down to the buttons, we can add a loading state to our Open Sale button, isLoading equals open sale transaction pending. While this transaction is pending, we also want to disable the Close Sale button.
[2:30] We'll come down here and update the isDisabled statement to say, or open sale transaction pending. We also want to give users a better indication of whether or not their transaction succeeded or failed.
[2:42] Let's go back to the top of the file. I'm going to add a new import here and say, useToast. Then I'll come back into our component and say, const toast equals useToast. Then we'll come back down to our openSale function.
[2:58] When the transaction is successful, we'll add a new success toast. Toast, we'll make the status success. The title will be "Sale is Open." We'll use the subtle variant and add some markup for the description.
[3:14] Let's add a link here so users can easily check out the transaction on Ether scan. We'll say, a check out the transaction on Ether scan. We'll link to Href equals https Rinkeby.EtherScan.IO/transaction.
[3:37] Then here we'll interpolate the transaction hash and we'll say open sale transaction.hash. We'll also make the target blank, and set the Rel to no follow, no refer. Now let's do the same for the Close button.
[3:55] Come down below. We'll add an onClick callback to the Close button. OnClick equals close sale. Come back up to the top. Add a new asynchronous function, const close sale equals async.
[4:11] Then inside, we're going to do a lot of the same things we did in the openSale function. We'll make a try block, add a catch, log out the error. Up here, we'll check that we have a connected contract again.
[4:26] If not connected contract, then return early. Create a new variable. We'll say, let close sale transaction and set that to await connected contract.close sale. Then we'll wait for the transaction to resolve.
[4:46] Await close sale transaction.wait. We're also going to need to add another loading state. We'll come up above. We'll say, const close sale transaction pending, set close sale transaction pending. We'll set that to useState false.
[5:09] Then we can come down to our closeSale function, just like before. In the try block we'll say, set close sale transaction pending is true. Copy and paste that. Once it resolves, we'll say it's no longer true, as in, it's false.
[5:25] We'll do the same in the catch block. Let's add toast notifications here as well. We'll come down to the openSale function.
[5:31] We'll copy and paste the success toast and paste it in close sale. This time we'll say, sale is closed. We'll provide the same link to Ether scan. However, it won't be open sale, it'll be close sale transaction. While we're at it, let's come down to the catch block.
[5:51] We can add another toast notification for when the transaction fails. We'll just have it say failure, and log out the error as the description. The status will be error. We'll come down to open sale and do the same thing.
[6:02] Inside the catch block, when the transaction fails, we'll show a toast. Then we can come back down to our buttons and add a loading state for the close sale button, isLoading equals close sale transaction pending. When this is true, we also want to disable the Open Sale button.
[6:21] We'll come up and say, disabled equals not isOwner or close sale transaction pending. If we come to the UI, we click Open Sale and confirm the transaction in metamask, we see we have a loading state and a disabled Close Sale button. There's the toast notification.
[6:39] If we try the same thing with close sale, confirm the transaction in metamask, we have a loading state and a disabled Open Sale button. There's the success notification.
[6:48] In summary, we were able to use our connected contract instance created with Ethers and call smart contract functions based on user interaction in our React app. Specifically, a contract owner can open or close our ticket sale.