Cyfrin Updraft: Full-Stack Web3 Development Crash Course

Cyfrin Updraft Courses · 7:41:20

Patrick Collins leads a hands-on Cyfrin Updraft course on building websites for smart contracts with help from AI coding assistants, stressing that developers must understand every line an AI writes. The first project is a plain HTML and JavaScript buy-me-a-coffee site that connects MetaMask and simulates, then sends, transactions on a local Anvil chain before moving to TypeScript. The second rebuilds TSender, an ERC-20 token airdrop app, in Next.js and React with RainbowKit and Wagmi, adds unit tests and Playwright end-to-end tests, and deploys a static build to IPFS through Fleek. The final NFT marketplace reads contract events through the rindexer indexer and blocks flagged wallets with Circle's Compliance Engine.

Speakers

Topics

From Cyfrin Updraft Courses, hosted by Cyfrin.

Transcript

Welcome to the ultimate bullseack web 3 blockchain AI and vibe coding full course. Here at Siphon and Siphon Updraft, we focus specifically on blockchain and smart contracts. We live and breathe blockchain and smart contracts. Sending a financial transaction when all you have to look at is a bunch of numbers and letters isn't the best user experience. So obviously to onboard more people to blockchain to more people to cryptocurrency. We need websites. We need full stack applications we need much easier ways to interact with these amazing smart contracts. And in this course you're going to learn how to do exactly that.

Now with the rise of AI, coding websites has actually gotten much easier. But as you get more advanced and start building more complicated apps, AI start to break down pretty hard. and for the foreseeable future you need a human to be able to actually understand what the code is actually doing fix it upgrade it improve it maintain it etc so in this course yes we are going to very heavily rely on AI for a lot of what we're working with here but we're going to make sure that you understand the fundamentals of these full stack applications so that when you want to rearchitect something when you want to make some advanced choice when you want to change something you don't have to spend years and years debugging because you actually don't understand what's going on under the hood This crash course is designed to give you the

fundamentals so that you can rock up to any blockchain or web3 hackathon, which there are many happening at all times, and launch a project with a successful MVP, with a successful website on top of your blockchain applications. So by the end of this course, you are going to have the skills, you going to have the fundamentals to take any blockchain application that you've worked on, you want to work on, or even somebody else's blockchain application and put a website on top of it, either a simple one or a more advanced complicated one. And we're going to give you all the different places to explore and learn and understand so that so that if you want to take this further with your journey, you can go deeper into the full stack world. You can go deeper into security, you can go deeper into smart contract, etc. Now, ideally, you have a little bit

of coding experience before you do this. However, so long as you work with a sufficiently powerful AI companion, you're going to be all set no matter what. And we'll give you some recommendations for an AI to work with going along with this course. Now, ideally, you also have a little bit of Solidity experience as well. But if you're more junior there and you're more on the full stack side and you've never worked with Solidity at all, that's okay, too. If you're watching this on Siphon Updraft, in the course description, we have a list of recommended courses to take beforehand. And if you're watching this on YouTube, there is a link in the description that leads to the GitHub repo associated with this course which will have the recommended prerequisites for this as well. All this is to essentially say a little bit of solidity and a little bit

of JavaScript is very helpful here but not required. As of recording, the next blockchain hackathon that's going to be launching is going to be the ETH Global Taipei hackathon which has up to $200,000 worth of prizes. this hackathon has $200,000 worth of prizes is because many protocols are still dying for technical developers, for blockchain engineers, for full stack engineers for the whole suite. So, we're going to give you the tools to rock up to any one of these hackathons, do well, and then potentially get hired as a full stack engineer, get hired as an AI prompting engineer, and just be very technical in the blockchain in the web 3 world. We're going to teach you how to build and think creatively using AI to create powerful products in the blockchain space that you can impress your friends,

use to build your portfolio, get hired, and so much more. In this crash course, we're going to be building three different projects. One with raw HTML and JavaScript to teach you the fundamentals of what's going on under the hood of a website. How does the blockchain even interact with a website? What is going on here? Then we're going to take it a step further and build a static site in React and Nex.js. This is going to be more powerful and and the website we're actually going to build is a production app today called the Tender app. It is it is a token airdropper application and you're going to learn how to build exactly that. And then finally, we're going to build a dynamic site in React and Nex.js with more advanced features such as the Circle Compliance Engine, Fleek Functions using an indexer, and so much more. I am

incredibly excited for you to be on this journey with us because I know you're going to come out the other side armed with the knowledge to be a force for good in this industry. So before we get started here, let's go over some best practices for working with Siphon Updraft and for working with this course as well, and then we're going to jump right into it. Let's get froggy. So, let's begin our journey by talking about some best practices. That way, you can get the absolute most out of this course and be as effective as possible.

Now, you're either watching this on Siphon Updraft or on YouTube. And I encourage everybody to watch this on Siphon Updraft because we've got a ton of features to make the learning experience that much easier for you. So, if you are watching this on Siphon Updraft though, there's a couple links I need you to be aware of. The first one in the top right is going to be the GitHub resources page. This will bring you over to what's called a GitHub repo or a GitHub repository or basically a site that has all the code and all the information and basically all the materials that you're going to need to learn everything in our curriculum. You could basically think of this as your bible for the duration of your blockchain developer journey.

Additionally, in this GitHub, there's a discussions tab right here that you can click on. And in here is where you can ask questions, discuss with other people taking the course, interact with members helping out, and it's where you can discuss anything that you're having trouble with. Then on the other side, you'll see this tab called written lessons. If you cannot stand the voice coming out of my mouth, you can just flip over to that and just read the course curriculum as well. If you prefer the written content over the video content, and it's good to go over to the written lessons anyways to maybe copy paste some stuff. If you scroll down here, right now it's blank, but if anytime there's an update to a video, something's changed in the video and we haven't swapped out the video yet, you'll see a little updates section with

information saying, "Hey, the video says this, but you should do this instead." Now, like I said, this is a very fastmoving industry and sometimes things change and sometimes things need to be updated. So, when you're watching one of these videos, be sure to look for the updates tab at the bottom. And then, additionally, whenever you're working with the code that we're working with, I will give you a link to the finalized edition that you can use as a reference as well to make sure that the code that you're working with actually is going to match what we are going to build. Additionally, if you think you found something that is different or doesn't quite work, be sure to make a discussion for it in that GitHub repo. Like I said, it's going to be your bible.

Additionally, there's a link to join the GitHub discussions. This is your platform to ask questions, engage with the community, and learn with both other people taking the course and also our TAs who are going to be helping you out along the way. Additionally, there is a link to the Discord for more real-time communication. I urge you to ask questions in the GitHub discussions as well because those are going to be indexed going to make them much easier to Google search later and have them show up as opposed to Discord. Discord is still phenomenal for you to join them. For those of you watching on YouTube, hello. you should scroll down to the description and in the description are going to be links to these resources as well and additionally a link to Siphon Updraft. If you've been watching, you've already seen some of

the advantages that Siphon Updraft has including written lessons, single videos, and there's also ways to track your progress instead of having to scrub on a giant YouTube video. So, for all of you who are watching this on YouTube, definitely be sure to go over to Cipher Updraft, sign up there, and watch the curriculum there because your learning experience will be much better. but leave this video playing in the background on YouTube so we get the bump from the YouTube algorithm. Thank you. That being said, as we go through this course, we're also going to teach you some best practices on working with artificial intelligence, how to best prompt these AI so that they can give you the best results. Just keep in mind they sometimes get things wrong and it's a good idea if you are going to use an AI to fact check it with a human or

another resource. So, be sure to say hi in the discussions and maybe meet some like-minded peers. And additionally, once we do get to the coding portion of this course, it's a good idea to code along with me as I'm explaining things. So having the video up as well as your coding screen is a good idea so you can follow along with me as I'm explaining it. If you're watching this on Siphon Updraft, you can just click the little video pop out button and have the video pop out as such and code next to it. All of this is to say if you run into an issue, jump to that GitHub repo and make a discussion. We will also be giving you some tips very soon about how to best make a discussion. Yes, asking questions to other human beings is a skill and we're going to try to teach you to be

the most effective because asking well formatted questions is not only the secret to being a fantastic AI prompt engineer, but also becoming an incredibly successful developer. We're going to learn how to ask well- formatted questions. And whenever we post on discussions or forums or whatever, we're going to work on formatting them as best as possible. Take breaks. I cannot tell you how many people have tried to rush through these courses and be like, "Oh, I'm going to finish in a single weekend." Your brain doesn't work like that. Your brain needs time to absorb the information. So, take breaks. Maybe every 25 minutes to a half hour. Take a 5-minute break. Or maybe you like working in longer chunks. Maybe take a whole hour and then take a 15- 20 minute break. Don't try to rush through the whole video in a day. You're not

going to retain the information. Go outside, go for a walk, grab some ice cream, get some coffee, go to the gym. Your brain needs time to have the information settle. Maybe every two hours just step away. Maybe be done for the day. Work at whatever pace makes sense for you. Everyone's going to have a different learning pace. There is no right speed for this course. I've had people take my courses in 2 weeks, in 3 months, in 6 months. It doesn't matter. Pick a pace that you can do and stick to it. Not only work at your pace, make sure that I'm talking at a pace that makes sense for you. There's a little gear icon in the YouTube video here where you can change the speed of how I'm talking and how fast the video is going. So, if I'm talking way too fast

for you, then you can slow me down. But if I'm talking too slow, then you can speed me up. And if you're watching this on Siphon Updraft, you have the same dials as well in the bottom right hand corner. Additionally, if English isn't your native language, we have seven different subtitles in the Siphon Updraft video player as well. So, make the adjustments you need to make me go the speed you want me to go. And of course, this course is modular, so you can bounce around topic to topic and go to where you want to go. If you don't want to do any full stack stuff, then skip that section. If you want to go right to the advanced stuff, do that.

Like I said, go the pace and take the learnings that you want to do. And after every lesson, it might be a good idea to go back and reflect on each lesson to really make sure the knowledge gets ingrained. Repetition is the mother of skill, and we're going to be repeating a lot of smart contract development. Now, the last bit here is in the Siphon Updraft platform, we're going to have quizzes that you can take to help see if you learn the knowledge that you were supposed to learn. If you're watching this on YouTube, you don't have that. So, go sign up for Siphon Updraft and then play the YouTube video in the background so the YouTube algorithm bumps us up. Blockchain development and open source development world is incredibly collaborative. So, be sure to use tools like, of course, the GitHub discussions tab, Ethereum Stack

Exchange, the decentralized Q&A forum, Piranha, issues on different GitHubs, artificial intelligence, and more. And like I said, we'll give you more tips on how to most effectively use these sites in the future. And the reason I'm putting so much emphasis on this and that I will continue to put so much emphasis on this is being a successful smart contract developer is more than just knowing how solidity works. Knowing where to go for information and how to collaborate with people is often more important than your smart contract knowledge because oftent times you're going to run into issues you don't know how to solve. So, we're going to teach you to unblock yourself on this and really anything in life. Plus, syncing with other people in the space makes it way more fun. So, those are some of the best practices to be really successful

with this course. But with that knowledge, you're now standing at the edge of the rabbit hole. And if you're like me, once you jump in, you'll just want to keep going deeper and deeper. I'm so excited for you to embark on this journey with us. and I'm looking forward to seeing you on the other side just like the thousands of other developers who have taken these courses and have emerged the other side triumphant. So yes, we are going to AI vibe code our way to victory. But in order to actually get the best out of vibe coding, you need to understand the fundamentals of what's going on because quite often AIs give bad suggestions that lead to huge issues down the line or are insecure and you end up having a security vulnerability in your

application. So every time we work with AI in this course, we are going to make sure that we understand what the AI is telling us. Now, which AI should we use? So for me personally, the Claude 3.7 AI is my favorite. However, some of the newer chap GBT models are great as well. In the GitHub repo associated with this course, we have a list of AI recommendations. These days, even some of the free models such as DeepSeek are really good and do a very good job helping you out, especially with Nex.js and React, which is what we're going to be going over here. There are still a lot of tools in the blockchain space that most AIs are still pretty bad at.

So quite often during this course, you'll see me not prompt the AI for a question about a certain tool because I know the AI isn't very good at that tool, and I know the AI is going to screw it up and give me a dumb answer. And this is already one of the key pieces of learning that I want to embed into you going through this. The better you get at understanding AI's limitations, the better you can actually work with it. Like everything we do in Cipher Updraft, repetition is the mother of skill. So working with these AIs more, getting familiar with the specific models, learning its limitations will make using the AIS even better. AIS are not a silver bullet. They're not a panacea. They are a tool just like all the other tools we're going to use in this course. So treat it as

such. Throughout this course, we're going to be teaching you how to build applications like what you see here with a or unis swap. These are applications which have a front end, which have a website, but they interact with smart contracts. they interact with the blockchain on the back end. So, anytime you're clicking buttons on the website, the website's actually sending transactions, sending requests to a different blockchain. We are going to start way, way, way from scratch. We're going to start all the way back with raw HTML and JavaScript completely from basics. So, this is where if you've never worked with HTML, if you've never worked with JavaScript before, perfect.

We're going to do a little bit of a crash course to get you set up here. Additionally, we're going to set you up with a coding environment so that you can be working with the exact same tools that I'm going to be working with so that you can follow along very closely with these videos and these written lessons. So, we're going to go ahead and get started jumping into section one, which is going to teach us a lot of fundamentals of HTML, JavaScript, and interacting with a blockchain directly from a website. Before we get into that, we're going to set up our VS Code or a lot of people like cursor. So, if you want to use that, you can as well. We're going to do a quick JavaScript crash course and we're going to do a little bit of working with an AI to get into

the flow of prompting and working with these tools. Now, the reason that I'm not going to be using cursor for my actual coding is number one, I think it's incredibly disruptive when it gives a lot of, in my opinion, stupid suggestions. And number two, and this is important, memory is the residue of thought from educational psychologist Daniel Willingham. And the reason it's important is that we remember what we actively think about and process. You've probably heard those sayings before about, oh, time stood still when such and such happened. The reason that time stood still is because you're thinking about something really, really, really, really hard. Often we think about something really hard when there's something emotional going on, like we're incredibly sad or we're incredibly happy. And the more we think about

something, the more we remember it. Or if you've solved a really, really hard problem, you thought about it so much that you remembered it a lot more. So when we're coding, especially for learning, I want you to be thinking about what you're doing as much as possible. I think it's great to ask questions, but especially when we're learning, I want you to ask questions. I want you to be specific about the questions that you're asking. Every time we run into a problem that we don't understand, that's good. I want you to think about the problem. Okay? Why is this happening? What's going on? The more you think about it, the more you'll be able to remember it. So, think about it. Try to debug it yourself. Try to solve it yourself. And then obviously, you know, we want to work with our AI buddies or with Google or with

discussions, etc. after we've spent a little bit of time ourselves thinking about it. That way, we can remember it much better. That way, in the future, we have that piece of knowledge in the back of our mind that we can use to solve problems even more efficiently. So, a lot of this won't make sense quite yet, but basically the reason I'm going to be using VS Code instead of cursor is I think cursor is very annoyingly disruptive. I want you to be very specific about the questions that you're asking. And cursor gives answers without you even asking a question. I want you to at least ask the question yourselves first. And this will also get you better at prompting AIS at asking AI questions as well. So, to get started here, let's go ahead. Let's get you started by getting you first set up with VS Code or

if you want to do cursor, whatever you want to do. Windows users especially, I want you to take some time to get set up here because you should 100% be using WSL and not your raw Windows terminal. So, let's go ahead. Let's get our tools installed and let's get started. The first thing you're going to want to do is download the Mac or if you're working with Linux, download the Linux installation of Visual Studio Code. Once you have it installed, it'll look a little something like this. And if it's

a fresh installation, it'll even give you some tips and tools to actually get started. If you've never worked with Visual Studio Code before, I highly recommend going through any get started or getting instructions tips that come with opening Visual Studio Code. Additionally, we have a Visual Studio Code crash course in the GitHub repo associated with this course. Hello, my name is Vasili and I'm going to be your instructor for everything related to development on Windows. Installing developer dependencies on Windows is a little bit tricky and sometimes really messy.

That's why we are going to install a tool called WSL or the Windows Subsystem for Linux. On the upcoming videos, I'm going to be really straightforward on how to install the tools we are going to use for the rest of the lessons. However, if you want to actually understand what WSL is, how it works, the basic command line tools, and a lot more, please be sure to check our 100% free crash course about WSL for smart contract developers we published on YouTube. With that said, let's get that Windows environment ready for development. Microsoft has definitely increased their support for developers in the recent years. But when it comes to smart contract development, there is

a better option to consider using WSL, the Windows subsystem for Linux. Trust me, it's a gamecher. You see, a smart contract development often involves working with tools and utilities that are commonly found on Unix based environments. While Windows has come a long way in accommodating developers, there can still be some challenges when it comes to running certain command line tools and setting up the right development environment. Not mentioning that if you want your code to run on any machine, using a Unixbased system, Mac and Linux is better for your developer needs. Here is where WSL shines. By installing a Linux distribution through

WSL, you can access to a full-fledged Unix like console right on your Windows machine. And don't worry, you don't need to be a hacker or wear a hoodie to make it happen. is actually quite simple. So, let's do it. Let's get started with the installation process. First, we need to install WSL using the Windows terminal. In Windows 11, you can just hit the Windows key and type terminal. Hit enter and this will open the Windows terminal for you, which defaults to PowerShell.

However, if you are using Windows 10, you are going to need to install the Windows Terminal through the Microsoft Store. So, you can open the Microsoft Store, look for Windows Terminal, and please be sure to select the one from Microsoft Corporation. Once you have it, please open it and installing WSL is going to be a really straightforward process. And on the terminal, we are just going to type WSL-install. This is going to trigger some admin level operations as you are going to see on the screen right now. You just select yes, hit enter and once installation process is finished it. This will require you to restart your computer.

Once you reboot your computer, this is going to automatically trigger this window to continue the installation. So, let me actually make this big for you to see. And the first thing we need to do here is to select our Unix username. I'm going to select Chrome. Hit enter. And then we are going to be required to set up a new password. Something interesting on most of Linux operating systems/ distros is that when you are using the terminal, if you type anything over here, the password is going to remain hidden. That doesn't mean that you are

not typing anything but the terminal is not going to show anything over here unless you use another Linux distro for example Linux min otherwise most of the Linux operating systems are going to keep this always hidden. I'm going to type my password though and then hit enter. And as you can see here, the terminal recognizes that password and ask me to type it again for confirmation. And once we finish this, that's it. We have WSL installed on our machine. As you can see here, the installation finished it properly and we can continue. I'm going to actually

close this for now. Now that you have WSL set up, you can go to the search bar and look up Ubuntu. You'll see either YUbuntu on Windows or something like Linux or an app like this. And if you click it, you start it up, you'll get dropped into a Yunu shell or a YUbuntu application. And it's in this type of shell that we're actually going to be doing work. Another way you can open this up search bar, you can look for the terminal application. And this is what a lot of coding tutorials are going to teach you how to use. Now, the first time you open it, it actually

might take a long time because because your computer booting up an entirely new operating system different from your Windows operating system, and that might take some time. When you open up your terminal, it will default you into this Windows PowerShell. This is not what we're going to be using. If anytime you see a PowerShell like terminal, that should be your cue to know that we should switch over to a YUbuntu or a WSL or a Linux-like terminal. In PowerShell, you can actually select the drop down here and switch over to YUbuntu in a new tab and it'll give you that same Ubuntu setup. Now, you can work directly off of your Linux/Ubuntu setup, but we're actually going to be doing most of our

work directly inside of the app Visual Studio Code. There are a number of ways to install Visual Studio Code. We'll let Basili teach you how to install Visual Studio Code because there are multiple different ways that you can get it installed. There are plenty of ways to install Visual Studio Code and I'm going to show you three of those ways on this video. The first one is going to be using the terminal as we are going to spend a lot of time using the Windows terminal. Anyways, we can install it with the hacker/programmer way. For that, we are going to use winget which is a package manager pre-installed on Windows 11 for installing software. You can also get a

winget on Windows going directly to the documentation. So for example, if I want to install Visual Studio Code using winget, I just use winget search VS code. This is going to show me all the available packages to install Visual Studio Code. So for installing Visual Studio Code using this is going to be really easy. I'm just going to copy this and say wing install and the package name I just selected. If I hit enter, this is going to start the installation of Visual Studio Code on my machine. As you can see, this is opening the installation for me. And that's it. This is how you install our code editor from

the terminal. And if I go to the main menu, I already have Visual Studio here installed. The second way to install Visual Studio Code is open your favorite web browser. On the search bar, look for Visual Studio Code. Select the one from code.bisudio.com. This is the official Microsoft one. You can click on download for Windows. This is going to automatically trigger the download. And open it. This is going to start the installation process. And of course, we have the user and license agreement.

Mostly no one cares about this stuff. and no one reset, but I highly recommend you to spend a little bit doing so as a good practice. I'm going to accept the agreement, hit next. This is going to give us the path and the installation is almost the same as we did on the terminal way, but in this case is going to allow us to have more things like this add open with code action to Windows Explorer file context menu. This will allow us to do a right click here and have the ability to open any folder using Visual Studio Code. I'm going to also create a desktop icon. Hit next and install. On the meantime this is installing. I'm going to mention that if

you are concerned about Microsoft having that much control over the data because they own Visual Studio Code, they own GitHub, they own a lot more of tools and you want a open-source version of Visual Studio Code that will lead us to the third way which is VScodium. BScodium is an alternative to visual studio code which is exactly the same but without Microsoft telemetry gallery logo and etc. So basically they take the basic visual studio code built remove all the Microsoft stuff from it and change the license to the MIT license.

So basically if you are concerned about your privacy or Microsoft having access to your data BScodium is a really nice alternative. The installation is exactly the same. You hit on install. You download the latest release. This is going to lead you to GitHub and select the one for Windows. In this case is going to be the one marked as BScoding user setup.xe. You hit this. This is going to trigger the download and you're going to have the package to be installed over here. If you open it, this is going to allow you to install VS Codium. But as this is not signed by Microsoft,

Microsoft is going to prevent to run this. You click on more info and run. Anyway, this is going to open the installation and the process is going to be almost the same because at the core they are the same code editor. And this is how you can install Visual Studio Code in three different ways. Select the one you like. For this tutorial and for the rest of the course, I'm going to keep from the one coming directly from Microsoft Visual Studio Code. Thanks, Vasilei. I'm going to go ahead and install it directly from the website.

Now, remember, Visual Studio Code is actually very different from Visual Studio. Visual Studio is a completely separate app even though I know they have almost the exact same name. We want to use Visual Studio Code. I'm going to download it directly from the website for my Windows application. Going to go ahead and hit save. I'm going to open it up now. Accept the agreement. Next. Next. Next. Create a desktop icon. Add open with code actions to the Windows Explorer. Next. Install. Finish. And let's go ahead and launch Visual Studio Code now. Right away when you open up

Visual Studio Code, you actually might even get prompted saying you have Windows Subsystem for Linux installed on your system. You want to install the recommended WSL extension for Microsoft for it and you can go ahead and hit install. Now, if you don't get prompted with that, you can also go to this extensions bit the left-hand side here and look up WSL in the extensions. And we want to install WSL here for Visual Studio Codew WSL. Make sure you see the little blue check mark to make sure that this extension is coming from microsoft.com.

Let's go ahead and hit install. Now that we have this installed, we can actually reopen up our Visual Studio Code with a Linux or a Yuntu terminal built directly into the Visual Studio Code. I'm going to go ahead and hit X here. What I can do is I can hit these three little drop downs and go to terminal. Select new terminal. Now what's going to pop up is very similar to our terminal application. Going to pop up in the PowerShell terminal here. Similar to what we did in the PowerShell application, we can hit this little drop down and switch to a YUbuntu WSL terminal instead. Again, this will take some time for it to actually start

loading up. If I type cd in the command line, it'll bring me to what's called the home directory. Additionally, at the bottom left, you'll see this little open a remote window section. We click on that. You can also select connect to WSL instead, which will open up our VS Code directly in a WSL yuntu setup. Now, when I select the three dots and go to terminal, new terminal, it'll open us directly up in a Linux-like or yuntu like terminal instead of PowerShell. So your cue moving forward is that anytime we're working with Visual Studio Code on your Windows, you should see this WSL

YUbuntu in the bottom left because we're always going to be working with YUbuntu or WSL or this Linux- like environment and not the Windows-like environment. If we close out of Visual Studio Code and then we reopen up in Visual Studio Code, we'll see in the bottom left that it's actually going to open back up in Yubuntu. And if we click here and we close the remote connection, you'll see the WSL YUbuntu goes away. We close Visual Studio Code and then reopen it. You'll see it's still gone. So, we want to open it back up. connect to WSL.

And now we're connected back in the WSL environment. Let's open up our terminal. And there's a shortcut with control shift back tick if you want to use that as well. And boom, we're back in our our Linux- like or our WSL like environment. For the rest of the course, as I just said, this is the environment we're going to be working with. And anytime you work with terminal commands, use the Linux or WSL or YUbuntu like terminal commands. Don't use the Windows ones. Anytime you're typing in a Ubuntu or Linux shell and a website says, "Hey, here's the Linux command. Here's the Windows command." If you're in this type of shell, if you're in this type of terminal, use the Linux commands.

Now, one of the awesome things about Visual Studio Code is it has this thing called terminals, which are command line prompts that allow us to run scripts. Basically, it's where we're going to be running all of our code. The way we can open up the terminal is we can go ahead and hit terminal and select new terminal. It'll get something like this. Now, you might have bash or zch or some other type of shell. the type that you have doesn't really matter because on Mac and Linux it's going to be Linux-based and like I said sometimes installing this can be the hardest part of this entire course. So, so don't get discouraged and please use Stack Overflow Stack Exchange Ethereum and the GitHub repo to move past any issues you run into. Now, if you're on Mac or Linux, you can actually hit control back

tick to actually toggle your terminal mode. This will pull the terminal up and down for you. Getting familiar with keyboard shortcuts will actually make your life a lot easier because you'll be able to move around Visual Studio Code much more effectively. We have a link to a list of keyboard shortcuts additionally in the GitHub repository associated with this section. As we move along, I'll give tip on different keyboard shortcuts that you can optionally use. Otherwise, you can just go ahead and click as well. You can click the trash can to delete the terminal. Go back up terminal. New terminal to pop it back up. Now, the next thing that we're going to need a little bit later, we're not going to need it for this section, but it's good to install it now, is going to be git.

We will have links to the installation instructions in the GitHub repository. Installing Git on Linux, you're going to use one of these two commands. And on Mac OS, if you just type git on the command line, it should go ahead and prompt you to install it. So, if we're back in our command line and we just type git, it should prompt you to go ahead install it. And if you do get- version, you should get something that looks like this. You can also use a Mac OS Git installer by clicking this link here and running through the installation process. All right, now that you have Git and Visual Studio Code installed, we can continue on to the next section. Awesome. If you're not planning on using Windows or Git Pod, feel free to skip the next two sections. So, now we're opened up in VS

Code. If you're on a Mac OS or a Linux machine, the bottom left hand side should just basically kind of look like this. If you're on a Windows machine, the bottom left should say like WSL or YUbuntu or a combination of the two. But now, from here on out, all operating systems can run the exact same commands, whether or not you're Mac OS, your Windows, or your Linux. We're always going to be using the Linux or Mac OS like commands moving forward. This way, the instructions will always work no matter what. Again, if we pull up our terminals here and we type echo dollar sign shell, it could say zsh, it it could say bash. There's many different types of shells out there that all work the same. Doesn't matter which shell that you're using because the commands that we're going to run will all work no

matter what shell you're working with. I've mentioned this a couple of times, but just to reiterate, often times my terminal here will get kind of bogged down with a lot of lines. And what I'll often do, I'll often do to clear it is I'll either type clear in the command line to clear all those lines or I'll type command K for Mac OS or this is also control K for Linux or Windows. So the shortcuts are the only thing that will be a little bit different depending on if you're on Mac OS or Linux. Additionally, the trash can and the X here are actually very different. The trash can here means to delete the terminal. And anytime I say delete the terminal, that's usually what I'm referring to. If I just say like close the terminal, I often just mean hide the

terminal. So if we were to do terminal, new terminal, let's pop up a new terminal and added a couple of lines here. If I were to hit the X here, I'm actually just going to hide it. I'm going to hide the terminal. But I could pull the terminal back up. Now, if I do terminal, new terminal, you'll actually see that now I have two terminals. If we scroll up here, we can see that this other terminal is the one with all the new lines and I just created a new terminal here. So I can I can trash one of the terminals and that one is now deleted. I can also create a bunch of new terminals, but typically most of the time we want to work with just one terminal. There are some scenarios where we want to work with two. Now to toggle the terminal to basically hide it and

then pull it back up, you could do control tilda on a Mac OS. On Windows or Linux, it's going to be control plus back tick. So command back tick for Mac OS, control plus back tick for Windows and Linux. And this will this is the same as hiding the terminal and then doing hitting view and selecting terminal. So again, command back tick. This will toggle hiding and showing your terminal. You can also do view terminal to hide it and then view terminal again to show it. And hiding it is equivalent to pressing the X button here. I'm on a Mac OS and I also use command J a lot, but that keyboard shortcut might not be

the same for everybody. Now, this is very different from the trash can icon here. If I hit enter a whole bunch of times and then I go ahead and do trash can and then I toggle or reshow the terminal, you'll notice that those new lines are no longer there. Whereas if I do a bunch of new lines, hide the terminal and then bring the terminal back up, those new lines are still here. This trash can means to delete and remove everything in the terminal or kill the terminal. Whereas the X is just to hide it. Now, let's get started setting up your Visual Studio Code so that it's going to be very easy for you to all your files and projects that are related to this course in a single folder. So, let's go ahead and let's

open up a new terminal again with control or command tilda or command J or view terminal or terminal new terminal or whichever command that is most applicable to you. Now, a number of the commands that we're going to be running are known as Linux commands, and they work on ZSH. They work on Bash. They work on pretty much any shell that comes with Linux or Mac OS, or if you're on Windows, Windows WSL. You can see what directory you're in by typing pwd. This will show you which folder that you're inside of. When I typed pwd here, I'm inside of users/patrick. I'm in the Patrick folder. This is known as your home directory. If you type cd, which stands for change directory, you will change

directory to your home directory. This little tilda stands for the home directory. I could also type cd cd tilda, hit enter, and then type pwa again, and you can see I'm in the exact same directory. It's cuz I changed directory to my home directory here. For all the Cipher Updraft course, I recommend you making a brand new folder specifically to hold all of your files and folders for this curriculum. Now, I highly recommend for this course you put all of your learnings materials in a single folder. That way, you can refer to your notes. You can refer to past code later. To this day, I still do that all the time. Even when I'm interacting with my AI agents, I will sometimes take a chunk of my code, dump it into an AI

agent, and say, "Hey, this is what I've done before." So, it's good to have all of your code in one place. And then, additionally, all of your projects you want to use to build up your GitHub repository so you can build up some street cred that you actually know what you're doing. And in fact, in this course, we will actually have you push some code to GitHub if you have never done this before. Getting a career in software engineering often means proving how good you are at something. And the only way to prove that you're good at something is to have projects and interact with and work with different protocols. So I'm going to create a new directory by pulling up my terminal which you can do terminal new terminal or you can do control shift tilda or command J or whatever you want to pull

up a new terminal and then you can type mkdir full stack web 3 cu and then we'll cd into our full stack web 3 cu and this will be the directory where we do all of our work. If you do a pwd, this will show you the full path of where you're located and you can make sure that you're actually in the correct folder that you want to be. If you do cd dot dot, you go down a directory and you can see where you're in. And I'll do cd full stack web 3 again. pwd. Boom. I'm back in this folder. When you're in here, what you can do is if you have the code terminal command installed, you can do code period. And this will open up a brand new window inside of that folder.

So now if I pull up my terminal and I do pwd, you can see I'm still in here. And on the left hand side, what I can do is I can type the command touch high.txt. The touch command is a Linux command that will create or touch a file. And on the left hand side here, we can see it did indeed create a file called hi.txt. This little file explorer button here will show you all the files currently in the folder that you're in. LS, which stands for list. I can see all the files in this folder. And I can still see them on the left side here. If you do not have access to this code command, you can do command or control O or file open, which will open up a window for

you to open. And we are now inside of this folder. First thing I want you to do is I want you to rename this high.txt. So if you hit this little file explorer button and you select this high.txt and hit enter, highlight everything. We could change this to readme.md. I want you to put in here my notes file. And you can put any notes in this. You can call it readme. You can call it notes. You can call it whatever you want to put in here. And this is where I want you to put any and all notes. Final thing that we want to install, and we'll install a lot of these as we go along, is actually an AI extension. Something in VS Code to help us actually have AI inside of our Visual Studio Code. If you select this little

box looking thing that says extensions, you get a search box which allows you to search for extensions. The AI that I work with the most is GitHub Copilot, but you can use whatever AI you want, or you could just not even use an AI. GitHub Copilot does cost money to use, so feel free to also not use it. There's some other free VS Code extensions that use AI, so feel free to browse around and choose which one you like. For me, I'm going to go ahead and install GitHub Copilot. You'll get a little guy in the bottom right for your VS Code that will let you know that you've done it correctly. You'll need to sign into GitHub, which is something that we've recently made, in order for it to work.

You might also want to install GitHub Copilot chat. This will give us a chat box for GitHub Copilot, where if we click the little GitHub Copilot icon, we get GitHub Copilot Chat, we get a little chat box similar to Claude or Chatbt right in our VS Code. Now, you'll see me using GitHub Copilot pretty often when I write my code. Because I have this little duad on down here, I can start writing some code and GitHub Copilot will start giving me suggestions as to what I should write. For example, if I were to add SPDX like sense identifier, GitHub Copilot actually automatically starts to gray out and give me a suggestion. If I hit tab, I can autocomplete my code with GitHub Copilot suggestion. You can go

ahead and click the little doodad, go to GitHub copilot chat as well, and you'll have a chat box very similar to something like chat GBT or Claude or whatever. You can also choose the different model by hitting this little drop down. And I know mine kind of pops off the screen here, but you can see there's different models that you can use. GitHub Copilot does cost money to use, so please use the AI of choice that you prefer to work with. Like I said, for me, Claude is also one of my favorite AIs and I will be using it pretty extensively. GitHub Copilot allows you to change your model as well if you want to change a model that you want to work with. For this, I'll try as much as possible to work with the free editions of these plans. And DeepSeek is going to be another popular option as

well. So once again, feel free to use whatever AI assistant that you find best. And you should use an AI assistant especially and I'll show you when and where to use these assistants. Now that we've done all this setup, now that we are about to code, and I'm going to stop you once more, I want you to actually take some time and write out why you're taking this course. Because guess what? Software engineering, even with the help of AI, is tough, especially in the blockchain world where this is one of the most adversarial environments on the planet.

Security, especially with these websites, is incredibly important. As of recording, just a month ago, there was a $1.5 billion hack that happened. $1.5 billion dollars was stolen from a project because their website wasn't secure. They had all these tools. They had all these AI things at their fingertips and yet they had a massive vulnerability and it resulted in the loss of $1.5 billion. So getting this stuff right, doing this stuff well, takes a lot of skill. And to get really skillful, it can take a lot of time and it can be hard. So, there will be times where this course gets hard. I'll try to be as funny as possible. I'll try to be as charming as possible to keep it

engaging. But there will be times when it gets hard. I want you to have a list of reasons that are so rock solid where you get tired, you get frustrated, you can fall back on these. Maybe you take a day, you take a break, you know, go with the pace you need. But when you need that motivation to not give up and not be a quitter, no matter how bad you want to just fall flat on your face and collapse, you come back to these and remind yourself. And the only thing I'm going to ask you is to be honest. Be honest with yourself. If you want to make a bunch of money, if you want to get a better job, write that. And I would even say be more specific. Maybe you might write down, I want to have a 100k salary in two years. You know,

maybe you're just starting college or you're looking to start college or maybe you graduated college or maybe you're down your luck or maybe you're already making this and you say, "I want to have a 200k salary in 2 years." Come up with a specific date. Come up with what you're looking to do. And even if it's not wholesome, that's fine, too. Maybe you just want a better life for your family. That's great. Write that down. Write down who, my wife, my sister, my brother, etc. Who are the people that matter to you? Why are you taking this course? Maybe you just want to be a part of the history in this amazing blockchain injury. Write down all these reasons why. If you want to go the extra mile, also write down what will happen if you don't finish this course, if you continue on the pattern of life where

you're currently on. If you don't get better, if you don't make yourself better, make this list of reasons. Save it here. And every time this course gets hard, and anytime anything gets hard in life, come back to this list of reasons. And maybe every six months to a year to two years, come back and update this list of reasons. Why are why are you doing what you're currently doing? So yes, I want you to pause the course, pause the video, fill this out, write why you want to complete this, why you must complete this, and then we can keep going. All right, welcome back. Did you actually write your reasons out? Well, you better have cuz I'll tell you what, the people that do do a lot better in this course and do a lot better in this

industry. All right. Well, now that we've got that out of the way, let's do a little bit of a JavaScript crash course to get started here. Now, AIS are phenomenal at JavaScript. Ready? Watch this. I'm going to ask DeepSeek to write me a JavaScript codebase that will tell me the square root of a number. And boom. It's given me the entire codebase for such a function. So, AI is really good at JavaScript. So, if you have a hard time with JavaScript, be sure to ask questions. Ask a lot of questions. Every single line of code in our code bases, we should understand and we should understand what it does. So ask your AIs and if your AI doesn't know, Google it, go on Reddit, go to the discussions, go to the other help forums that we've given you in this course.

Now, a lot of what we'll be working with is we're going to be working with this project called NodeJS.js is a JavaScript runtime environment. Now, that's really confusing, but basically the JavaScript that runs a website like kind of what you're seeing here is different from the JavaScript that can run a file or run a server. So, JavaScript on a website just kind of works. To do any JavaScript in like a script, you need to download a runtime environment that can run JavaScript. So, in order for us to continue, we want to download Node.js. Downloading Node.js is, in my opinion, the hardest things to do to even get started here. Same as installing VS Code. If this takes you a little bit to install, no worries. Take your time. If this takes you a whole day to install,

don't sweat it. We can use our terminals in our VS Code to actually install this. If you go to the nodejs.org website and install it, it'll give you a script to actually download and install it here. So, check out your operating system. If you're on a Windows machine, you should be using WSL. So, use the Linux script. If you're on a Mac OS, you can go ahead and use the Mac OS script. I do recommend using nvm. And you can use npm. For this course, we're going to be using PNPM. So if you want to change this from npm to please go ahead and do so. This will take some time, but once you run that script, you should have two what's called executables in your terminal. So you can go ahead and trash your terminal and restart your terminal just to make sure it's a new terminal.

And you should be able to run node- version and see an output that looks something like this. You can go to the pmppm.io site to install PNPM as well. All right. Well, now that we have Node.js installed and npm installed, we can do a little bit of a JavaScript crash course. Once again, if you already know the basics of JavaScript, feel free to skip this section. But let's go over a couple of things real quick. So, if we're in our full stack web 3 cipher updraft folder here, if I open this up to the side, I can create a new file by right-clicking, hitting new file, and I'll create a file called my code.js. In here, I can write some

JavaScript code. For example, I could go back to DeepSeek and I could literally copy this whole thing that it gave me and paste it in here. And we would see this massive thing here. And if I pull up my terminal, I can actually run this code. by typing node and then the name of the file mycode.js. Remember, you need to be in the same folder where that file exists. So, if I type ls, I can see my code.js there. So, I know that I'm in the correct folder. And now I can run node my code.js. And we see the square root of 25 is 5. So, if your AI gave you something else, don't worry. You know,

I'll show you some other stuff in a second. If I change this to 16, we'll see how good the AI did. We'll run this again. Square root of 16 is four. Great. Now, I'm going to delete everything in here and we're going to start from scratch. Pretty much anything I want in here, any valid JavaScript, I can do console.log. Hi. Console.log is an example of printing stuff to the terminal. So, if I pull my terminal now, let me clear everything in here. I pull this down here. I'll run node myode.js. JS, it'll just say hi because I told it to console.log hi. I told it to log the string hi to the console. I can say console.log by run node myco.js and now

it will console.log by to the console. Whenever you see these quotes, this is known as a string. A string is basically just some alpha numeric text. You can have many different types in JavaScript and you can save them as something called a variable. So I can say let my var equals hi and this is me saying I now have a variable called myvar which represents hi. Now if I do console.log myvar and I run this node my code.js, it'll now print hi because I'm doing that console.log the log thing again, but instead of having high directly in

here, I have myvar, but myvar represents high. So, this line is the exact same as if I just did this. Now, you'll notice a lot of times in JavaScript that these little semicolons are at the end. I hate these semicolons, and this is one of the frustrating parts for me about JavaScript is that it lets you do kind of whatever the hell you want, and that can sometimes result in really weird scenarios. But, I digress. For some reason, we decided to let the world run on this language that is very bizarre. But you'll see how bizarre it is much later on in the curriculum. If you do let it means that we can actually change this. We could do my var equals hello.

So it was hi and then we changed it to hello. Now if we console.log out, you'll see we now get hello. But if we did a const instead of a let, if I tried to run this now by running this again, we're actually going to get an error. And the error is assignment to a constant variable. And this is already where working with AIS is really helpful for really simple errors like these. Often what you can do is you can copy the error, go to your AI, say, I got the following error. Shift enter. Three back ticks. Shift enter. Paste that whole error. Shift error. Enter. Three back ticks. These three back ticks tell your

AI that whatever is inside of these back ticks is going to be code or something associated with code. Do shift enter twice. Say what is going on here? How do I fix this? Enter. And it'll actually tell us right away what the error is. The error occurs because you're trying to reassign a value to a variable declared with const. If we go back to our code, we can see that's exactly what happened here. We have const my var equals hi and then we try to reset it to hello like what we did before. The const keyword stands for constant which means it cannot be changed. So we actually can't go from myvar hi to myvar hello

because myvar is a constant. So deepseek is exactly correct. Const creates a constant unchangeable variable reference. So we actually cannot change it to this. So what we could do is we could do what's called commenting out this lines. Now on a Mac I can do command forward slash to comment out this whole line or you could just you know type forward slash slash and then a little space here then hit command s. Now, before we can run this, if you see this little white dot here, this means that this file is not saved. I'm going to hit command S or you could hit control S or file save. In order for us to run this, we always have to save it.

If I have console.log high and I were to write console.log by and I were to run node my code.js, it still prints out hi even though it clearly says by. But the reason for that is because I forgot to save it. So I'm going to hit command S or control S or file save. Now it's saved. I no longer see that white dot. Now if I run this, I actually correctly see the buy in here. So these two slashes mean that this line will not be considered code. We can do two backslashes and write whatever the heck we want here. And JavaScript will basically say, "Okay, cool." And skip right over that. Comments are incredibly helpful for us because we can say this

next line does you know xyz or like you know prints out my var. These are helpful for us as humans. These are great places to take notes and as you code I highly recommend you write comments in your code so that you can refer to these later and know what they're doing in your own words. So now with all these comments we have myvar equals hi. We have this line commented out. I can now run node my code.js. And actually what you're seeing here is I'm typing my co and hitting tab. And my terminal will actually autocomplete this. And I hit enter. It now is typing out hi. We can have lots of different

types in JavaScript. I can say const true false equals true. Now I have a variable named true false which is set to what's known as the boolean true. It can be true or false. I can do a console.log true false. Now let's see what this prints out. Obviously it prints out false because this is false. I can also do console.log type of true false like this. Let's clear my terminal. Run this again. Above is not defined. No. And this should be lowercase. Now I'll do node my code.js and it'll say hey true false is a boolean. A boolean means it's a true false. Now I said const true

false equals false but I put these parentheses around it. Now when I save this clear my terminal rerun it now says it is a string. So this true false variable this here is a string. It's a text. I can also say true false equals 1. Save it. Clear my terminal. Rerun. And now it says type of true false is a number because one obviously is a number. We can have functions in JavaScript. For example, I could say function my funk little parenthesis here and then little squiggly brackets console.log I. If I run this right now,

nothing will happen because a function is basically just a subset of code that can be run whenever it's called. So right now, this my funk is basically representing this console.log high and says, hey, anytime you run my funk, I'll print out hi. So now if we take this my funk and place it here, don't worry about the parentheses or the semicolons for now. They'll make sense later. Now, if we run this code, zoom out just a little bit. Node my code.js. Now, it'll print out hi because what we're saying in our code here is we're saying, hey, we have a repeatable chunk of code where all we do is console.log high and

anytime you call this function, we will call console.log high and then we call it one time. What's funny is if you put this at the top, clear and then rerun, it'll still run high because JavaScript is smart enough to just like look down the file and say, "Oh, okay. You defined it later." If we were to call my funk a bunch of times, you know, like one, two, three, four, five times, and we ran node my code.js, we would see hi, hi, hi, hi, hi. Also, another quick little terminal tip. If you're in the terminal and you hit up, you can very quickly cycle through your most recent commands up and down. And this is one of the quickest

ways to kind of just run a command you just recently ran. So node my code tojs. Hi hi. Great. Now we are going to be working with TypeScript and once we get to the TypeScript parts, we'll talk about TypeScript there. And if you already understand TypeScript, you can obviously skip that section. But I do want to talk about asynchronous programming and a couple other things real quick. So, we're going to watch a video that I made on this topic a few years ago. So, the terminal will look a little bit different than it does now, but all the content is still exactly the same. So, let's give this a [Music] watch. But I do want to give you a quick

background on asynchronous programming. So far, the programming that we've done has been synchronous. And Solidity is an example of a programming language that's synchronous. Synchronous means it just goes one line after another. If our code looks like this, this is synchronous, right? Our main function is the first thing that actually gets called and we do console.log, let variable equals 5, and then console.log again. This is synchronous programming in JavaScript. All of our Solidity is synchronously programmed. There will be some exceptions to this when working with oracles, but for now everything is synchronous. JavaScript can be asynchronous, meaning that we can actually have code running at the same time. Good example I like to use to understand the difference is with cooking. In synchronous programming for

cooking, you might put popcorn in microwave, wait or popcorn to finish, and then maybe you'll pour drinks for everyone. And now this is synchronous programming. Now it might be a little bit weird for you just to put the popcorn in the microwave and then just stare at it waiting for it to finish and then pour the drinks. You typically can pour the drinks while your popcorn is in the microwave. And this is where asynchronous programming comes in. If setting up for this movie night were asynchronous, what you would do is you'd put popcorn in the microwave and while the popcorn is in the microwave or drinks for everyone and then you'd wait for popcorn finish since there's nothing

left for you to do. But it doesn't make sense for you to wait for the popcorn to finish to pour your drinks. You can just go ahead and and pour the drinks right away and then wait for the popcorn to finish. So asynchronous programming is a way for us to do stuff without waiting around for things to finish and this is really advantageous and JavaScript by default allows us to do this asynchronous programming. However, sometimes we do want to wait for our popcorn to finish. For example, if instead of just pouring drinks, maybe the next thing instead of pouring drinks was place salt on popcorn. Of course, if we want to place salt on our popcorn, we do have to wait for the popcorn to get out of the microwave. So even though placing popcorn in the microwave has this wait time, we need to be able to

tell our code, I want you to actually wait for it or know you're good. You can go on and do another task. So that's kind of the difference here. And I'll leave some links in the GitHub repo associated with this course to understand this a little bit better. Functions that have functions that come with this waiting period return something called a promise. If put popcorn in the microwave was a function in JavaScript, it would be a promise-based function. A promise can be either pending, fulfilled, or rejected. And this is how we know if our popcorn is done. If our popcorn was a method, putting popcorn in the microwave were a function in JavaScript. When we're waiting for the popcorn to finish, it's in a pending state. When it's when the popcorn is finished, it would be

fulfilled. And if we aborted halfway through and we stopped waiting, it would be rejected. So, putting the popcorn in the microwave returns a promise. With this promise, we have to tell our code, hey, we want you to wait for the popcorn to finish or you can go ahead and you can just keep doing stuff. So, let's put this all together with some JavaScript syntax here. Let's say again we're going to be setting up this movie night and we need to cook popcorn, pour drinks for everybody, and then we need to start a movie. So, let's write some pseudo code to pretend what this code would look like if this was actually a function.

So, we'd create some function called setup movie night. And in here we would say, okay, what's the first thing we need to do? Okay, we need to cook popcorn. So let's say we have some cook popcorn function. So we'll say, okay, cook popcorn. Then the next thing we're going to have to do is we're going to have to pour drinks. Pour drinks. So we'll call some pour drinks function. Now, here's the thing. We only want to start the movie once our popcorn has been cooked and once our drinks have been poured. So if either one of these returned a promise. So if either one of these returns a promise like cook popcorn for example, we would need to tell our code here to actually wait for the popcorn to finish because cooked popcorn is going to be a function where

we could say let status equals cooked popcorn. And while the popcorn is being cooked, the status is going to be pending. Once the popcorn gets cooked, it'll be fulfilled. If the popcorn breaks, the microwave explodes, the status would be rejected. But we don't want the status to be in a pending situation before we move on. We only want to start our movie once these two functions have completed. And let's say both of these return these promises things. So we need to tell our code, hey, you have to wait or you have to wait for cooked popcorn and for pour drinks to finish. So, what we can do now, without getting too deep into the weeds on how all of this works, one of the easiest things that we can do, and you'll see us use this syntax quite

often, is we'll turn this function into an async function. When our functions are async, we get access to a keyword called await. The await keyword tells any promise-based function to wait for that promise to be fulfilled or rejected. So we say, okay, we want to await for our popcorn to cook and then we want to await to pour our drinks and then we can just go ahead and start the movie. And we only start the movie here once these two have been completed. And this is why throughout all of our code, you'll see this await keyword used a lot, but it can only be used in async functions. So basically whenever you see this await keyword just know ah okay the function that's being called is promise-based and we don't want to move

on to the next step until that function has completed. So that's a little bit more about promises and asynchronous programming. Hopefully that's clear. If not like I said there's some links in the description to learn more about asynchronous programming. The reason I wanted to go through this is because most of the functions that we're going to be working with are going to be asynchronous. For example when we deploy a contract what do we have to do? Well, we have to wait for it to be deployed. If we don't use synchronous programming and we just leave our our function main like this, what would happen is we would write some code like contract.deploy and we wouldn't wait for it to finish. Obviously, if we don't wait for it to finish and we try to run

some code on a contract that hasn't finished deploying yet, it's not going to work. So, we want to do this. We want to have our main function be an async function so that we can we can wait for our contracts to deploy. We can wait for things to happen. We can wait for our popcorn to finish. We can have the flexibility to tell our code to to either wait for our popcorn to finish or continue on. So now that we have our main function as an async function, uh we're going to add some code to our main function down here. And the code that's added here is some syntax. We're working with asynchronous functions. And if this part is confusing, I'm just going to say for now, absolutely don't worry about this. If you want to try to understand it later, that's fine. But for those who

are following along, we're just going to add it at then catch the error error. You can follow along typing this yourself or you can just copy paste it into your code. Basically what this allows us to do is we have our our main function that's an asynchronous function. So when we call the main function, this is some other syntax for waiting for it to basically finish and then printing any errors it gets. And that's why we do this. But again, if this big lump of code, if you're like, what is going on with this big lump of code? Honestly, for now, just copy paste it. So great, we have our asynchronous

function main, we have some code, and then we have this lump of code, which basically just calls our main asynchronous function. Okay, great. I'm going to delete all this for now, but you can still find that comment in the GitHub repo associated with this course. Awesome. Okay, so now that we have our real basic setup, let's go ahead and start coding. And if if this setup part is confusing and the async away stuff is confusing, don't worry too much about it. Uh it'll make more sense as we progress. Now, the final piece of the crash course is all websites, every single website you interact with is essentially the combination of three things. JavaScript, HTML, and CSS.

HTML is what the site looks like. And if I rightclick a site and hit inspect in this elements tab, I see this dock type HTML. And this is essentially the code of what this website looks like, what is being displayed. If I go to sources in here, I can see kind of all the different files that represent this website. And in some of these files, there are these.js js files which stand for all the logic. So the HTML is essentially what the site looks like.

The JavaScript is the logic of the site and then there's something else we'll talk about later which is the CSS which is kind of the beautifification of the site. HTML is the layout and the look of the site. JavaScript is the logic of the site and CSS is the beautifification of the layout or you can almost think of CSS as like what it looks like. Number two, when we build a protocol like what we're going to be building in this course, it's the exact same thing. HTML, JavaScript, CSS, that's it. But there's a lot of tools that we can use that make rendering and interacting with and working with these websites much more functional and much easier and much better. But a really basic HTML site, if we rightclick, new file, my

site.html, we can create and we can actually make a minimal site ourselves. Right now, we type exclamation mark little carrot here. Doc type HTML. I'll put a little backslash HTML. And in here, I'll just say body. Uh, in my VS Code actually autocompleted there, a little backslash body. I can just say hi in here. And this is a valid website. This is valid HTML for a website. If I click on my site, what we can do now is in our extensions, there's an extension called live server. And if you install this, you'll get this little go live

button in the bottom right. Now, what we can do is we can actually just be on our my site.html, select go live, and it'll spin up a little server for our website, which it just says hi. And then I could go back. I could change this to buy a little mark. remember to save it. Go back and it now says bye. Now, now we can click this little click to close server button and it'll stop the server. And now if we refresh, it'll just say, you know, site cannot be reached. Now, optionally, because HTML is purely HTML, one of the cool things you could do is you could rightclick reveal in Finder or open in folder or whatever you want to do. And you could even just doubleclick

on your site itself and it'll still render. And in the URL bar, you'll see kind of the path to your file in the URL bar. Now, this is different Windows to Linux to Mac, etc. So, if you're on a device where this doesn't work, sorry. Too bad, I guess. Just use the live server feature instead. So, that's it for our crash course here. We will be walking you through actually putting JavaScript directly into the HTML. And like I said, as we go along, if any of this is confusing, please be sure to use your AI of choice to help you understand what's going on. I'm using DeepSeek for this because DeepSeek is free and does a really good job for being a free model.

All right. So, with all that being said, let's finally jump in to our HTML/JS buy me a coffee raw website where we're going to learn how to interact with a blockchain, build a website that interacts with a blockchain, and learn some of the key features to make sure our site does what we want it to do. So, this has been the JavaScript crash course. Let's now jump in to the actual site. All right. So, if you scroll down in the GitHub repo associated with this course, you can see all the code associated with what we're going to be working with here. Now, I'm going to do a little walkthrough of what the final product is

going to look like because this is going to be something that you're going to build from scratch and we're going to be working with AI to help us build some boilerplate to help us go a lot quicker. But essentially, this is what the final project is going to look like. And if I go to my index.html, I can hit the go live button in my bottom right. Or once again, if you prefer, you can open up this index.html file in your browser itself or whatever you want to do. And this is what we see. We see some connect, get balance, withdraw amount, and buy coffee. This app is going to be connected to a smart contract in the background. It's going to be connected to a blockchain where the smart contract

is very minimal. It has a couple of key functions like fund which we're going to be mapping to this buy coffee button which when we select this buy coffee button, it'll essentially call this fund function on our smart contract that will be actually on the blockchain. In our terminal, we're going to have our own locally running blockchain with this mark already deployed and with some addresses with some money in it that we can actually interact with our front end with with our website. What we can do on our front end is we can go ahead and select connect and this will have a little pop-up showing up saying connect with MetaMask. We can edit our networks and just choose a new network that we're going to add called Anvil. We'll hit

update, connect, and we'll connect as such. And what we can do is in our MetaMask, we're actually going to change to a little test address that we're going to add to our MetaMask here. And what we can do is we can rightclick, hit inspect, go to the console, and to make things easier, we had a lot of stuff being printed out to our console here. So, we're connected. We can select get balance, which will get us the balance of a smart contract. We can buy coffee or basically fund this smart contract with one ETH that our anvil one is the account that's actually connected. Then we can call this buy coffee function.

We'll select the button. MetaMask will pop up saying hey uh do you want to actually send this? It'll have all of our data. We'll go ahead and confirm. We'll get a little console output here saying transaction processed with the hash of the transaction. Now, when I select get balance, it'll show that we now have a balance of one in this contract. I'll hit buy coffee again to fund it with another ETH in this contract. Transaction will go through. I'll hit get balance. Well, I'll process the blocks one more. I'll hit get balance now. And we see we now have a balance of two. I can also optionally withdraw all the funds out of this smart contract. Transaction will go through.

If we get balance now, it actually says three. It's cuz I got to I got to mine a block real quick. You'll understand what this means in a second. But now, if we hit get balance, we do indeed see that the balance of the contract is zero and we have withdrawn all the funds out of the contract. So, this is what we're going to be building a very simple application, but we're going to learn all the fundamentals of actually interacting with our blockchain application through a MetaMask or through whatever browser. We're going to learn how to connect, how to make calls such as reading the balance of a smart contract, which is going to be a function on this Solidity smart contract. So, we're going to learn how to interact with these contracts that are on the blockchain. Additionally, fund smart contracts and call different

functions like fund, etc., and map them to different buttons on the website. This is an incredibly important section because this is the fundamentals of how all websites that interact with blockchain work. Understanding the fundamentals really well here will set you up for success for everything else you do and for making sure that your applications are actually secure. So with that being said, let's go ahead and let's build this application. So you're in your VS Code or your cursor or your text editor of choice here and we're going to go ahead and for me I'm on the home directory of this full stack web 3 siphon updraft directory that I made. You can of course be you know wherever you want to be. I'm going to go ahead. I'm going to create a new folder mkdir html ts coffee copy

siphon updraft and I'm going to do code html ts coffee copy siphon updraft to open this up in a new window. You can of course as we showed before do file open to open this up in a new window as well. Whatever you'd like to do. We can open our terminal and type pwd to make sure we are indeed in the folder that we just created. To get started, let's go ahead create a new file with a right-click new file. And we'll type a little readme.md. And we'll write the task out that we are going to do. So, here's what we're going to be making. And it's good to write out one of these, a product spec, if you will, where you're going to write out exactly what you're want to make. So, what are we making? We're going to be making a minimal HTML/

JavaScript site. And we will eventually show you Typescript in this section as well that has the following buttons which map to the Solidity smart contract. connect, buy coffee, get balance, and withdraw. And what's good about writing this out is oftentimes you can even just take this product spec, flip over to your AI agent, and say, "Here is my product specification, do those three back ticks to show that this is delimited from the rest of it, and then I could say, could you please make me a basic HTML template with this?" And most of the time AIS will be pretty good at doing this. And of course, because it's AI, it's giving me all this

formatting and all this blah blah blah. It's already making this way more complicated than I want it to be. But if you copy pasted this, it would probably work. And if you want to work with your AI to figure out, you know, what is everything it's doing, great. Feel free to do so. For us, we're going to go ahead. We're going to start from scratch. And we're going to be using AI to help understand what's going on kind of quickly for us here. So, I'm going to rightclick, new file. We're going to call this index.htm. HTML and this is going to be our homepage. At the top we want to do a little carrot here and VS Code comes with this little dock type um carrot here exclamation mark dock type HTML and then we'll do a little closing tag at the bottom. So in HTML everything

is like a tag. So you have an opening tag and a closing tag. You kind of think of it as like an open parenthesy and a closing parenthesy, but they're tags instead of parenthesis. Oh, and then we want to do HTML and have everything inside of this tag here. And this just says, hey, this document is of type HTML. Once again, for those of you who skipped the intro, we're using the live server extension on VS Code, which gives us this little go live button, but you can, of course, open this up directly in your browser if you would like to as well. So, we have this high thing. We want to give this a little title here.

So, we'll do a new tag. And GitHub Copilot's actually helped me out here a lot. Give me an open tag and a closed tag. And I'm going to say title, buy me a coffee title like this. I'll save it. Now, if I come back and I refresh, I now see buy me a coffee little tab for my project here. Cool. So, now let's set up a little bit of boiler plate. We'll do another little body tab here. And this body tags is going to be basically kind of the meat of the site. And we're going to create some buttons. So in HTML, you can do this little button tag. And you can say like hi in here. And now we have a little button we can press. Right now it doesn't do anything obviously, but we

want to make a few different buttons. So we want to say a button. We'll give it an ID equals connect button. We'll call it connect and have a closing button tag. So now we have a connect button and this is what we want to get started with. The first thing we need to do is we're going to need to connect a wallet to this site. Now if you do not have a wallet already, uh you should cuz we went over it in blockchain basics. But if you don't have a wallet, like I don't have a wallet here, you can download MetaMask or Rabby or whatever you want to use. I highly recommend you use MetaMask so that we're working on the same software wallet here. If you don't have MetaMask already installed, please

take take some time to install it here. If you do have MetaMask already installed and you have funds in it, I highly recommend that you create a new MetaMask for this course so that you 100% know that you won't accidentally put real funds into this and this will always be separate. Now, if you're on Chrome or a Chrome like browser, what you can do is you can actually add a new user or add a new account and create a brand new account without a MetaMask so that you start completely fresh for the duration of this course, but whatever you want to do. Once you have MetaMask installed, you'll want to pin it to your browser so that we can access it pretty quickly moving forward. So, great. So, now that we have this basic HTML up and

we have everything kind of set up, first question we want to ask is, okay, how are websites connecting to the blockchain here? How does this work? Well, in the little demo I showed you, hey, uh, we can click these buttons, we can do these functions, and the MetaMask really felt like the gateway to the blockchain. So, I'm going to show you a short video that I recorded a few years ago where once again, the fundamentals are still 100% the same. what is actually going on when your MetaMask connects to your website. So, let's go ahead and watch this video which will explain what's going on under the hood of connecting to a blockchain. All right, so here we are with a website or a front end on top of some smart contracts that we've

deployed. Doesn't really matter what it is right now. This is typically the interface that you'll see boiled down to a really really minimalistic level. Typically, you'll see something like connect function, right? and MetaMask or some other wallet connector thing will pop up. We'll hit next. We'll connect here. Might even say something like connected and we can also execute functions. We can interact with our smart contracts. We can confirm etc. Right? This is something you might see something like a right. I'll hit connect on the A application. It'll say, "Hey, how would you like to connect? I'll choose MetaMask. I got to change my MetaMask to mainet." But you get the picture, right? This is a a simple example of what that would look like.

So, what is actually going on in the browser when we connect? What is actually going on? And what do we actually need to do? We're going to rightclick, hit inspect, and on the right side, we're going to see our our debugger here. Now, if we go over to sources on the top of our browser, you'll see a few things. If you look down over here, right, we'll see this URL, right, which right now is going to be my local host. and we'll also see MetaMask and Phantom and a whole bunch of other stuff. These other things that we see here are going to be what's injected from our browser extensions.

The reason we see this MetaMask thing here is because I have MetaMask installed, right? The reason I see Phantom here is because I have the Phantom app installed. MetaMask of course being an EVM wallet and Phantom being a Salana based wallet. Now what happens when we have these extensions installed is they automatically get injected into a window object in JavaScript. And in fact if we scroll down to here in the console again you can find console. You can click it here. You click anything up there and we type in window. We'll see we have this big window object with all this stuff.

Right? This window object represents this our window basically. Right now, if we scroll all the way to the bottom and we do window.ethereum, we also see an object here. Now, this window.ethereum object only exists if you have a MetaMask or MetaMask like browser. Or if you want to look at some other web 3 wallet, you could do window.solana. Solana, right? And we see this window.solana. Now, let's look at a browser that doesn't have MetaMask or Phantom installed. What do you think is going to happen in the window? Now, let's go ahead rightclick, hit inspect.

We'll go to the console. Now, let's see what's going on in here. If we go to sources, we first off, we don't see that MetaMask or that Salana source here. And if we go to console, we still see window. If I let me zoom in a little, we still see window here. But if I do window.ethereum, we get nothing. Or if I do window.solana, we also get nothing. So, in order for our browsers to know that there's a meta mask or that there's a phantom, those extensions automatically add these to our window objects. And that's something that we can check for in our JavaScript. The reason these wallets are so important is built into them underneath the hood, they have a blockchain node connected to them. And in order to interact with the

blockchain, we always need a node. And you might have seen URLs from Alchemy or Infura because you need them to interact with the blockchain. Alchemy and Infura are examples of third-party blockchains that you can interact with and basically rent, right? But you need them to create a provider or a node to send your transactions to. So you could do it in JavaScript like something like this is the Alchemy documentation where you you take that Alchemy URL, you stick it into some object and use that to send your transaction. This is a way that you could do it in the back end. But on the front end, what you normally want to use is you just want to use the user's MetaMask or their Salana or their wallet as the main wallet. Now, there are a ton

of other different types of wallets to connect like Ledger, MW, Coinbase, Wallet Connect, etc. And there are different ways to set those up, but they all do the same thing where they expose some URL. They expose some node under the hood. They give us that URL. They give us that provider. The way MetaMask does it is with window.ethereum. Boom. This is now our URL. This is now our connection. In fact, if you go up to your MetaMask, hit the little three dots, expand view, hit add network, and then just hit the X so we can get to networks. You can see all of these blockchains that I have in here all have an RPC URL. This is the

HTTP RPC URL connection of the blockchain node that's running. I happen to have one running locally right now. All of these also have a node RPC URL and you can actually see them right in your MetaMask, right? This is connected to Infura. These are all connected to Infura. These are it's all the exact same thing. MetaMask just has a really nice way of taking that URL, sticking it in the browser for us in this window. Ethereum or window.solana, Salana, you know, or whatever. So, this is the main thing that we need to know. We need we always need a connection with the blockchain and these browser wallets are an easy way to do that. Make sense?

Great. Let's take this knowledge now and let's apply it. All right. Welcome back. The key here on these newer MetaMasks anyways is in these networks, you know, here I have add a custom network. We're always going to have this RPC URL. And this RPC URL is literally just an endpoint that will connect to a blockchain. And anytime our website makes a request, it's secretly making some type of API call or HTTP post or HTTP get call to that RPC URL here. So when we connect our wallet to our MetaMask here, we're essentially giving this website access to that RPC URL inside of our MetaMask. So let's go ahead. Let's get that set up for our

website here. So, we have a connect button here, and this is where we're going to actually finally write some JavaScript. We could put our JavaScript directly into the index.html, putting it in between these script tags, but what I like to do is actually separate it into its own file. And I'm going to make a file called index-enjs.js. This is because at the end I'm going to show you TypeScript. and we're going to transition to TypeScript for the rest of the course, but we're going to start with this index-js.js, which is a little confusing. And what I'm going to do on our

index.html, so I'm going to say script src equals dot slashindexy type equals module. Don't worry about that for now. And then back script. Oops, it already did it for me. Great. What this line does is essentially says, "Hey, grab all the code that's in the index.js and put it into our site." So, for example, if in our index.js, I put console.log. Hi, and I saved it, went to the front end, rightclicked, hit inspect, I go back over to this console, and I do a refresh. I see it says hi. I keep refreshing, and it'll keep saying

hi. I can go back to my code. I could say hi a whole bunch of times. I could save, refresh, and it says hi a whole bunch of times. So, this line injects JavaScript directly into our HTML here. And this is how we can add logic to our connect button, right? Cuz right now we hit connect and it does absolutely nothing. So, we need to give this button some logic. So, in our index.js, let's go ahead, let's create that logic. So in here we're first going to get that connect button. So I'm going to say const connect connect button equals document and this document is a JavaScript keyword basically saying kind

of this document here document.getelement by ID and I'll say connect button. So we gave this button an ID of connect button. So it's saying document.getelement by ID connect button. Now in our JavaScript this connect button variable represents this button here. So what we can do now is we can say connect button do onclick equals and then we can do something here. I'm going to say connect and I'm going to create a function function connect like this. And I'm just say console.log hi. So this code basically means this variable now represents this button here. Here, let me move this up

to the top to make it easier. Connect button.onclick equals connect. This line means anytime somebody clicks the connect button, we're going to call this connect function. And here's the connect function. And all it does is console.log high. So, if we go to the front end, every time I click this, we should see hi show up over here. And that is indeed what we do see. And we see this kind of number, you know, keep going up. Every time I click it, it goes up up up up. Refresh. Goes back to zero. Boom. And that's working great. Okay, awesome. I want to put this back at the bottom because uh at the end we're going to do that. Okay, great. So now we have this connect function which right now is printing high. But we want this to connect to our MetaMask. Now we saw in

the little video before that if we do window.thereum enter. Great. We see this little object. So this is what we want to go ahead and connect to. But we first want to make sure that somebody has MetaMask or some type of wallet. So what we want to do is we can do first say if type of and just roll with me here for a second window.thereum not equal undefined we could do console.log I otherwise we could say connect button inner html equals please install MetaMask. This basically says take that

connect button and change the text to this. So on a browser with MetaMask installed, if I connect right now, it'll print hi. But if I go to a browser that doesn't have MetaMask installed, like this one, the text changes to please install MetaMask. But on a browser that does have MetaMask installed, connect it says hi. Okay, great. So we have a way to actually check if somebody can even connect at all. And if they can connect, we'll do some stuff up here. So, if they can't connect, please install MetaMask.

If they can't connect, do whatever we say here. Next. Now, my AI is already starting to give me some suggestions of what we want to do. And we could hit tab and try to do what it's saying here, like window.ethereum.request. Our MetaMask has this request button where we can send it this ETH request accounts, which would request. However, this window.ethereum Ethereum object comes with a lot of functions that we can call like request. But what if somebody rolls up with a Salana wallet or a safe multisig or a hardware wallet? There's lots of different types of wallets that can connect to a website. Window.thereum is kind of a specific keyword for MetaMask. So, it's great if they have

MetaMask installed, but we want to be able to handle more types of wallets that connect. So, the tool that we're going to be actually working with is this tool called VM. Like I said, this is a JavaScript tool that's going to help us build and work with these contracts. Another tool you can use is something like Ethers. You can use either one of these. We're going to be working with VM because later on it's going to be VM under the hood that's going to be powering some of our more complicated apps. So, I want to show you now. Now, right at the top, it's going to say, "Hey, here's how you get started." We're actually going to skip that because our website is just a raw HTML.js site. So, we're going to import our VM project directly from the website itself. And if we hit get started in the

documentation for more advanced projects, we're going to do this setup. But for now, I'm going to show you a different way to work with this VM project. So, for us to connect to MetaMask, we're going to use this VM tool. Right at the top of our index.js, JS. We're going to do import this little squiggly bracket here. And we import this thing called create wallet client from it's going to be https/sm.sh.vm. And we can even copy this esm.sh.vm. Oop, excuse me. SlashVM, notv. Could even copy this. Paste this in our browser here. And we can see that

importing this line actually imports all of these lines. Now, if we wanted to be even more specific, we could find all of these pieces of code and then copy paste them into our project here. But for now, we're just going to make our lives a little bit easier and do it like this. But if we wanted to be even safer, having this line of code here means that we're relying on this ESM.sh SH website to actually give us the correct code. If we wanted to be safer, we would just go and copy paste the correct code into our website ourselves.

But for the simplicity of the tutorial, we're going to do it like this. Now, this create wallet client, if we go to the VM documentation, go to the search bar, paste, and create wallet client and see how this is actually used. We can go down to clients and transports where it'll tell us a little bit about what these clients and transports are. Now, as of recording, AI is pretty terrible at VM. So, you ask your AI questions about VM, it'll probably get things wrong. So, for example, if I go over to DeepSeek again and I were to ask, hey, what are the three types of clients in VM? Let's do that. What are the three types of clients in VM? the

JavaScript blockchain tool. It probably won't know. I take it back. It does know. Wow, that's fantastic. Okay, cool. So, never mind. You can't ask me questions about VM. That's great. Well, there are these three different types of clients in VM. A public client which provides access to public actions such as get block number and get balance. A wallet client which provides access to wallet actions such as send transaction and sign message. a test client which provides access to test actions such as mine and impersonate. We are trying to connect our wallet to the website. So we're going to use a wallet client. That's why we're going to use this create wallet client function. We can look at the

wallet actions in the documentation. And we can see wallet actions are actions that map one to one with a wallet or signable Ethereum RPC method like ETH request accounts and ETH send transaction. to connect our wallet to the website. It's this ETH request accounts function that we want to call to do that with VM. We're going to do create wallet client and we have to pass kind of this weird syntax here. We're going to say transport which is going to be hey what is the blockchain that we're actually going to connect to? We're going to say custom window.ethereum and we need to import custom as well from VM. And this is basically going to say, okay, we're

going to have a wallet client. We're going to create a wallet that's connected to our MetaMask. Now, we could use this function to connect to different types of wallets, but for now, we're going to say just window.ethereum. But at least having this line in here means that in the future, we could add different types of transports. We could add connecting a multisig, etc. For now, because we're only working with MetaMask, we're using this. Up at the top, we'll say let wallet client like this. This will create a wallet client variable and we'll say wallet client equals create wallet client and then we'll do wallet client request addresses. Now, I know I'm probably already confusing you, but just stick with me. I'm about to explain

everything. And I also purposely put a bug in here, and I'll show you why in just a second. So, I know this was a bit confusing, but essentially all this code is doing is we're saying, okay, we're going to create a wallet client. We're going to create a variable that is going to allow us to interact with a blockchain wallet and our website. We're going to call it wallet client. And now we're going to call request addresses. And this is the function that basically says, hey, uh, what addresses can I work with? Can I actually connect to you? So that's it. Now, if I zoom out for a second here, we still have on click equals connect. So let's go ahead and try this. And if you want to pause and figure out why this is going to fail,

you might be able to do that, but you might not as well. Let's go ahead and hit connect. We'll do a refresh. We'll make sure we're running on our server. Yep, we sure are. All right, connect. It's not doing anything. What the heck? Why is this not doing anything? Well, let's look at this request addresses function in the documentation. And this would be something you could ask your AI as well. says, "Request a list of accounts managed by a wallet." Says, "Sends a request to the wallet asking for permission to access the user's account." If we look at this example down here, notice anything different about this. Huh? It's using this await keyword. So, if I scroll over this request addresses function, you can see it says request addresses as this little

parenthesis here. this weird arrow syntax which is a JavaScript thing but this weird arrow syntax means that this request addresses function returns and this is what it returns. It returns a promise that eventually gets resolved to this type. So since this is a promisebased function we need to add this await keyword and we need this to be an asynchronous function. So I'm going to change this from function connect to async function connect. And now I'm going to call await wallet connect request addresses. We need to add the await keyword to say hey don't just give me the promise like actually run the code that I want you to run and

you know let's get on with it. So now that I've added that I've saved it. I can refresh my front end. You can see what the issue is. And just one other note um I'm using Brave and since I'm doing developing I'm going to want to turn the Brave shields down for the moment. So for these demo sites, these locally running sites, we want to make sure we have these shields actually down for these so that we can actually interact with our application here. So now that the shields are down and we've changed this to an async function, if we run connected now, this will work kind of. But let me cancel and let me show you something. I'm going to refresh now.

I'll go back here and I'll do console.log high. Save it. Come back here. Now I'll hit connect. Huh? Wait, it printed high before I actually connected here. So, let me go ahead and cancel now. I'll refresh. And why is this printing out high before I finish connecting? What's going on? So, we make this async function connect. And we have this be await connect.reest addresses. Now, if we rerun this, I'll hit connect.

Doesn't get printed out because right now we're awaiting for this to finish running. So now, if I hit connect here, once it does connect, we do indeed see printed out. So, we want to make this an async function so that we can await for these wallet interactions to happen. And that's generally best practice for everything that we're going to do here. So for us now instead of console log hi we're going to do connect button.in HTML connected like this. I'm going to save.

I'll refresh. I'll hit connect. And now we see we are indeed connected. Now if we click our MetaMask click this little button here we can change which accounts are connected and we can also change which networks are connected. So, if I want to change accounts, I got a whole bunch of different accounts in here. I could change the accounts that are connected. I could change which network is connected. Or I could just disconnect completely. If I disconnect completely and then I hit refresh, it'll now not say connected. But if I connect again, connect, it'll now say connected. Now, if I refresh again, it doesn't say connected. But if I hit connect,

MetaMask doesn't pop up. This is because there's a whole bunch of logic that goes into connecting after refreshing that we're not going to focus on here. Basically, if we wanted to custom build it, this connect function would be absolutely massive. And we're not going to deal with that. We're going to have some package deal with that for us in a later section. But at this point, what's really cool, I go back here, click this little thing. I can now hit a connect button, and I can now connect my MetaMask to my website. And because my website now has access to a MetaMask, I can now start sending blockchain transactions and reading from the blockchain. This is so exciting. All right, cool. So now we have our connect function.

Great. What's next? Well, this is where we consult the readme. We have created a way to connect our wallets. All right, great. So now let's figure out how to buy some coffee. So, we're probably going to need to make a button to buy coffee, and we're going to need to call a function on a smart contract. Ooh, that means we're also going to need to have a test blockchain that we can call. Yikes. Okay, there's a bit more going on here than what meets the eye. Well, let's go ahead and let's get cracking away. So if we go back to the GitHub repo associated with this course, we scroll down in here at the top, we see this is a website for the minimal code

from the foundry flow course in specific the fund codebase which can be found here. So if we look at this smart contract, we go to the SRC folder. You don't actually have to understand Solidity for you to be able to read the functions here, but it does help. But essentially, if we scroll down in here, we have this function called fund, which is going to send money to this smart contract. It's going to send Ethereum to this smart contract. And we're going to call the fund function to do this. Now, if you decide that you don't want to become a smart contract developer and you don't really want to understand most of this, well, that's fine. You can pop this into your AI and say, "Hey, uh, what's this do? What are the functions I need to call?" Or if you're working with

a smart contract developer, you can just ask them, hey, like what do you want the buttons to do that the website's going to do? You don't really even need to understand Solidity. You just need to understand what functions you're going to call. So this fund function is going to send money to our smart contract that's deployed on the blockchain. And we want to read how much of a balance this smart contract has. How much ETH is in this smart contract. Remember, even smart contracts can have a balance of ETH and also a balance of tokens. They can have a balance of anything. Most smart contracts also are wallets themselves. So, we want to create at least two more buttons here. One of them is going to be a balance button. So, we'll say button ID equals balance

button. And then we'll also have a form to buy coffee to buy coffee or call this fund function. And you'll see why we need a form in a second. So now if I save this, I go to our front end again. I now have a get balance function that doesn't do anything. Now let's also create a form which will allow somebody to enter an Ethereum amount and we'll call the fund function and basically buy coffee for this smart contract address. So first I'm going to do this label label for equals ETH amount. Basically this is just going to be a label for our

ETH amount input field which we're going to create next. And so unlike a button, we're going to say input ID equals ETH amount placeholder equals 0.1 backslash close thing. Now, uh most of the time for tags, we're going to put stuff in between like an open and close tag. But if you have nothing to put in between the tags, you can just put like a little slash right at the open tag. Basically, this says, "Hey, this is an open and close tag." But this input field now, if we go back to the site, it now gives us this little bar to input stuff. And there's a placeholder of 0.1.

If I change this to like 1111, hit save. Go back. It's now 1111, etc. Let's undo. Hit save again. And now, finally, we'll do a button. ID, excuse me, button type equals ID equals fund button. And this one will say buy coffee back slashbutton like this. Now if we go back to the site, we now have a get balance button that does nothing. A label for our input here, an input amount where we can input how much coffee we want to buy. And then a buy coffee button. And obviously none of these actually do anything yet, but cool. So we have all this functionality

here. Now we want to go ahead and build it. Well, let's go ahead and let's get started with the get balance. This way that when we actually, you know, fund or buy coffee for this smart contract, we can actually check if the balance has indeed gone up. So, so since get balance is going to be zero until we are actually able to fund it. Let's create the fund or the buy coffee function first. So, at the top we'll do const fund button equals document.getelement get element by id fund button. Remember because on the index.html we gave this the ID of fund button here. And down at the bottom we'll say fund

button.onclick equals fund. And now we'll create a new function called fund which will do the actual logic to call this fund function on our smart contract. So let's create this fund function. So, let's go ahead and do async function fund. Now, we could do a regular function, a non-async function, but as we saw above, we're probably going to be working with a lot of promises. So, it's just going to make sense for us to do async right from the get- go. And the first thing we want to do is figure out, okay, how much money are we funding the smart contract with?

And we want to basically get how much is put in this little input field. So I want to get what's in here. And to do that in my JavaScript once again at the top I can say const e amount input equals document.getelement by id e amount like this right because on our index.html we have input id equals e amount. Boom. So this is the ID. So we want to get the amount of money that's in here. And our function we can say const eamount equals eamount input do value. This actually gets the the value of whatever is in

this input field. And then we can say console.log little back tick. This will allow us to put variables inside of a string. Funding with and then a little dollar sign squiggly bracket paste ETH amount. It now says okay anything that's dollar sign squiggies replace it with a variable. So now if I save this and I go back and I put a one in here, buy coffee, it now says funding with one, which is what we want. Cool. So now we have funding with whatever is in this field here. And now we can call the fund function. So just in case somebody hasn't called connect again recently, we're going to want to redo this line

here. We're going to actually let's redo this whole thing. We're going to copy this whole thing, paste it in here, but instead of doing await wallet connect request addresses. We're going to delete all that. And we're going to create this wallet client. Basically, we're going to connect to MetaMask again. And now that we're connected to MetaMask, we can actually simulate calling this fund function. The reason we want to simulate it first is we want to make sure that when we call this fund function, the transaction actually, you know, passes and goes through. So what we can do so now let's go ahead and continue with this. We'll do the awaitclient request addresses like this and that's good. So now when we call

fund we'll go ahead and be connected. Now we need to go ahead and actually call the fund function on the contract. Now, if we go back to the VM documentation, we scroll back down in here, go back to the getting started, we go back to the introduction on clients and transports. Like we learned about before, the wallet client provided access to wallet actions such as getting access to the wallet. When we send a transaction, this is actually going to be a wallet action. However, before we send the transaction, we want to simulate it. We want to simulate the transaction actually works and will actually go through. That way we don't send a request to the user's wallet.

That's going to now to simulate a transaction. We actually want to use a public client. So if we select a public client, we can see how we can create a public client. So now we'll go back to our codebase here and we're going to want to get this create public client function from VM. So we're going to import it. create public client like this. And now after we get and create the wallet client, we're going to want to create a public client. And I like to just have it out here like public client. But you could just as easily have it inside the function. But since all of our functions are going to be sharing the same clients, I like to just keep it outside like this. But now what

we can do is we can say public client equals create public client. And this is actually going to have the exact same setup as the wallet client where we're just saying, "Hey, the transport or the node, the blockchain node that we're going to connect to is inside of the window.ethereum." So that RPC URL is inside of it. And that's where we're going to create our public client. Now, this public client, if we go back to the docs here, if we look up simulate contract, there's a function here called simulate contract, which simulates/v validates a contract interaction. This is useful for retrieving return data and revert reasons of a contract write function. This function does not require gas to execute and does not change the

state of the blockchain. It's almost identical to read contract but also supports contract write functions. So basically we're going to simulate funding the contract with ETH. And then if the simulation passes then we can actually call the fund function ourselves. So what we can do is we can say await public contracts simulate contract and in here if we go back to the docs we scroll down we can see the usage it takes an address the address of the contract we want to interact with the AI which I'll explain in a minute the actual function that we want to call and the account that's going to be calling the function. There

are some other parameters we can pass in here as well, including the chain, how much ETH we want to pass, so on and so forth. So, back in here, we can add those pieces here. So, first we want to get the address. What is the address that we want to interact with here? Well, this is where if you were building a site yourself, you would want to ask the smart contract team, hey, where is the smart contract that we're going to be interacting with? And they would tell you. Now, because most contracts are actually deployed on different chains, it's a good idea to have some type of file or strct that tells you where contracts are deployed on different chains. If you

deploy a contract to ZKYNC and you deploy a contract to Ethereum, they're going to have two different addresses. So, your website is going to need to handle what chain the person is currently on and get the contract address depending on what chain that they're on. for us. Since this is just a demo site though, we're going to create a new file called constantsjs. And in this file, we could put like, you know, const e address equals, you know, and the address of ethereum. We could put const zk sync address and then the contract address on zk sync, etc., etc.

For us, we're just going to be working with a locally running blockchain. So, we're going to work with a single contract address. For now, to make life easier, if you go to the GitHub repo associated with this section, if you go to the constants js, I already have that contract address in here, and you can go ahead and actually, you know what, just copy the whole file for now cuz I'll explain what's in here. So, you can hit this copy raw file. Go back to here and paste it in here, and I'll explain what's going on. So, first off, we have our contract address here, which is going to be this one. Since we're only going to be working with the local blockchain, this is always going to be the contract address that we're working with here. And we're going to export it

so that other files can use this variable. The next thing we're going to need in here is called the AI or the application binary interface. Now, this is essentially just a specific format that tells codebases what functions are available on a smart contract and how to interact with that smart contract. Most of the time whenever you compile smart contracts, the AI is given to you as well. We'll see in one of our later sections, we will actually manually get the AI from some compiled code. You need this AI anytime you want to interact with a contract for the most part. There are some very low-level technical ways

to interact with a contract without an AI, but for now, just assume anytime you want to interact with a smart contract, you're going to need the API or the application binary interface. So great. So now that we have this contract address back in our index hyphenjs, we can import that. So up at the top we could do import contract address from dot slashconstancejs.js. That's this file right here. So we're going to import this variable. Now that we have the contract address, we can go back to our function and we can say, okay, the address is going to be this contract address. We could also have just as

easily done like let contract address equals, you know, uh, and pass the contract address in here. But this constants setup is something we're going to reuse in the future. So it's good to have it in its own file. So great. So address is going to be this contract address. What else do we need? Well, next we do need the AI. So let's go ahead and import that as well. This object notation here. And the key and value are the same. You can just do this. But if that's kind of confusing to you, just be like, okay, the AI is going to be our ABI. So the AI of simulate contract is going to be this AI here.

And if that's too confusing, you could even go back and call this coffee AI. Copy this. Make sure to save call this coffee AI here. And then instead of you can say AI coffee AI. That's probably a little bit clearer here. Next, it's going to be function name. What name are we calling? Then we want to get the account that's going to be simulating this. We're going to be calling on whatever account we connect to the website. So to get the account when we do await walletclient request addresses, this actually returns a list of addresses. So we can say const connected account equals this. And we put in these

little brackets here because this actually returns a list. And this is kind of a shorthand way to just say, okay, the first item in the list. We're going to call connected account. And so in our function here, we can say account. It's going to be connected account. And then we just have to pass the chain and the value. Now, here's something I don't love about this VM thing. I would love to just kind of pass like an RPC URL here and it'd be smart enough to know how to do this but VM as of recording you have to define any custom chain that you create. So for the purpose of simplicity, if you want to create a repo associated with this section, go to

index-js, scroll to the bottom, there's this function async function get current chain that we're going to copy. And in our codebase, we're just going to paste it in here. So essentially what this does is since we're only working with anvil, we're going to define this anvil custom chain. And basically we're going to say, hey, the current chain is just going to be called custom chain with whatever chain ID the current chain has. So, uh, kind of annoying that we have to do this for raw VM. You'll find that later on we won't have to do this. So, if this isn't making too much sense, don't worry, it's it's not really important. But in our simulate contract, we do have to add a chain here. So right

below where we make the uh connected account, we'll say const current chain equals await get current chain and we need to pass the wallet client in to get it. But now in our simulate we can pass the current chain and finally we can pass the value which is going to be this ETH amount here. Except there's an issue with this on our UI. If I put one in here, I want this to represent one Ethereum. Now, one Ethereum is represented in code as this much way.

So, one way is the smallest unit of measurement of Ethereum. And this way unit is how most applications understand, you know, talking about Ethereum amount. So if I were to pass the ETH amount here and somebody put a one in the front end, they would be thinking we're doing one way which is like 0.1 ether. So what we want to do is we want to actually get the ETH amount. So what we can do is there's a function built into VM called parse ether where it'll convert the Ethereum amount to its way equivalent. So we can just wrap this like so. And if we want to see it, let's

go ahead and even just comment this whole thing out. What I could do here is I could do like a console.log parse ether eamount. And then the UI I'll do a little one buy coffee uh funding with one defined chain is not defined. Oops. Get current chain. Oops. That's because we actually need to get define chain from VM as well. So, comma define chain. Now, let's try one again. Funning with one. And we see this is the representation of one ETH, right?

And this is what we want. It has 18 zeros. And this represents one Ethereum. 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8. Boom. This is how you represent one Ethereum in the blockchain units. Anyway, so let's delete that. We'll uncomment this. And now we actually will have a way to simulate this transaction on the chain that we're connected to. So let's go ahead and let's run this. And we're going to get an error.

Make sure we're connected. Great. I'm going to hit buy coffee. Uh and I get an error. I get this crazy output in my console. If I scroll up, I can see contract function execution error. the total cost of executing this transaction exceeds the balance of the account. Well, it's because I'm connected uh with one of my dummy MetaMask accounts and I'm also currently on the Ethereum chain. That's not where I want to be. I want to be on our little fake chain that we can use to interact with this contract. So, how do we do that? Well, let's go back to the GitHub repo associated with this section. We scroll down here. Remember there's this FundMe codebase and if we click on this this is

all the solidity code associated with this FundMe contract and in here there's this script called deploy funme.soul soul when the smart contract developers deploy this contract and they'll run this on Ethereum on the CK sync on Anvil on whatever they want for us you know we could say hey smart contract devs where did you deploy this contract so I can actually test it in my uh and they could say oh cool we deployed it to Ethereum and here's the Ethereum address and you could 100% you know get this hooked up to Ethereum and test this on Ethereum but that would really suck for you because if you had to test this on Ethereum or even on ZK Sync you'd have to spend real money to make sure that your website worked correctly. You know, maybe you said, "Hey, after this

transaction goes through, then do this cool animation on the side." It would be awful if you had to send real transactions to test that out. So, we want to run our fake dummy blockchain and we want to run it with this smart contract already deployed. So what smart contract developers can do and what they should do is deploy it to an anvil instance with saved state. So in the GitHub repo associated with this section, there's this file called fundmean anvil.json. And if you look in here, it's kind of this massive file of seemingly nonsense. But this represents the entire state of a fake blockchain

with our contract deployed to it. So what we can do is we can actually copy this whole file and in our codebase we'll create a new file called fundmean anvil.json and paste this whole thing in here and this whole file represents the state of the blockchain. So what I can do now is I can run a blockchain node connect it to this file and basically have a blockchain that gets populated automatically with contracts already deployed on it. And I know it's kind of abstract here, but let me take a step back here. So, first off, before we go any further forward, you need to make sure you have this Anvil tool installed.

So, if you type Anvil, you should get an output like this. And if you do command C, you can kill it. If you do not have Anvil installed, you need to go to Foundry book. Again, a link to this can be found in the GitHub repo associated with this section. And you want to go ahead and install it with, you know, whatever is in the installation. Once you install it, you should be able to run anvil-helpp like this. When I run this keyword anvil and hit enter, I get a fake little blockchain running in my terminal. And this is great. And what I can do is I can add this fake little blockchain running in my terminal to my MetaMask. So down at the bottom, it says listening on this address here. I can copy this and I can go to my MetaMask,

go to my accounts. I can even expand the view here. We go to the networks. We'll do add custom network. And we can add a custom network to connect to our little fake blockchain here. I'll call it Anvil. For RPCRL, I'll hit add RPCRL. I'll paste that address in here. And then at the front, I'll do HTTP col. And then RPC name. I'll call it Anvil. The chain ID will be 31337 or whatever it says or the chain ID of Anvil in your terminal. The currency symbol will be ETH and there will be no block explorer. I'll hit save. And now in my networks I now have

this Anvil chain that I can switch and connect my MetaMask to. That's pretty exciting. Well, what else? Anvil comes with some available accounts with a thousand ETH in each one of them and private keys associated with each account. So private key zero obviously is associated with account zero here. So what I can do now is in my accounts I can select accounts do add account import account. I'll copy this private key paste it in here. I'll hit import. And now I have this account in here imported. And if I'm on my anvil chain, it says I am a super bajillionaire. I have so much money. So amazing because

if I go to tokens, I have 10,000 Ethereum in this account on the Anvil chain. So now on my website, my MetaMask, I have an account that I just imported from Anvil connected to Anvil with all this money that I can use to interact with this website. If I control C and kill this, you know, my MetaMask will start to, you know, have issues like I won't be able to send anything because the blockchain is down and uh it's going to get real confused. So, we can restart it. And this blockchain is actually empty. So, what we can do is we can actually populate it with the contents of this fundme/anvil.json. So, like I said, the

smart contract developers were nice enough to deploy the contracts to this JSON file essentially and then give it to us so that we could test our website. And this is how you want to be testing your website. It's not ideal to be testing your website with mainet funds and with real things. You can also optionally use a forked network instead. So you could do Anvil if the code is actually deployed to a real network, but for the most part, it's usually going to be much cheaper, much faster to use Anvil where the code is already deployed. And if the smart contract devs have architected their project in a good way, if they've followed along with Cipher and Updraft, it should be relatively easy for them to do this. So

now that we've downloaded this file, what I can do is I can do anvil- loadstatef fun.json and this will start up a new anvil instance. However, it's going to be connected to this fundmean.json. Now in this JSON file, if we go back to our constants.js, js. If we click this address and we go to this JSON file, if I do a control F for this, I can see that something happened at this address. There's a couple different hits here. And this is because we've actually deployed a contract to this address. And if you want to be real cool with it, you can even see, you know, what the data was associated with this

address and everything that's going on here right in the JSON file. But now that we've started this blockchain on this Anvil instance, and what's kind of cool, by the way, is you can hit the X, which will hide the terminal, but if you pull the terminal back up, you can see that it's still running. So the X hides it. If you do the trash can, you'll actually kill it. And and if you pull the terminal back up, you can see it's no longer running. So I'm going to hit up twice to rerun that. And now the instance is running again. Now that we actually have this chain running, let's see if we can actually simulate this contract. So I'm going to put a one in here. I'm going to

make sure that I'm connected. Well, I'm actually not connected. So let's go ahead and get connected. I'm going to hit edit here. I'm going to make sure that I'm connected with my imported account, the one from Anvil, right? Should be F39, FD or whatever. We'll hit update. For networks, we'll want to just go ahead. We'll say, "Okay, let's only be connected to Anvil here." Only be connected to Anvil here. We'll hit update. Connect. Now we're connected. And if you're not, you can go ahead and click your MetaMask. Hit this. You can always edit these as I just did. Let's only connect to Anvil, only connect to the account that we just imported. And we'll go ahead. We'll select buy coffee for one ETH. And now we don't get that error. And this is because we simulated

the contract. And this actually passed. But like what did it pass with? Well, let's find out. Let's say const request like this equals await publicclient.simulate contract. And then we'll do console.log request like this. Go back to the website. We'll put the one in here. Buy coffee. And now we get a response. So, what happened was our website actually pretended to fund the contract and it got a whole bunch of data back about what happens when we simulated calling the fund function. And since calling the fund function in its simulation passed, it says, hey, this looks pretty safe to me to actually

call. And what's cool is it gives us this request object back which we can use to directly then go ahead and call the contract. So first we're going to simulate calling the contract and then we're going to actually call the contract. And this is really healthy because if this passes we have some assurance that actually calling the contract will pass. To actually call the contract with the exact same parameters here is we do await wallet client.right write contract and we pass in this request object and this returns a hash and we could even do a console.log hash. So this will be like a transaction hash that we could show to

the user saying hey your transaction is processing. So now what we could do is on the front end I'll put one ETH in here. We'll select buy coffee. Our MetaMask actually pops up because this simulation passed and it says, "Hey, this request looks good. Here's all the details you need for the request." You can now call write contract on the wallet client, not on the public client, on the wallet client, which makes this pop up and says, "Hey, uh, do you want to send this transaction?" And we can go ahead and confirm. And what we see is the transaction went through and we got a transaction hash back. And you can see in your little node that a transaction

actually did go through. Now, uh this next part, keep this node running, right? And if you if you pause the video or or you turn off your VS Code soon, uh just just redo that part when you log back on. So, make sure you've bought coffee, you've sent this through cuz as of right now, we have bought coffee and we have sent ETH to the contract address. So, now we want to check, okay, did we actually send how much did we send? What is the balance of this contract address that we just sent money to? How do we actually get the balance?

So let's very quickly do the get balance function and then walk through that whole process again to make sure that uh the money is actually going through. What is the balance of this smart contract? So what we can do we have the balance button. So index hyphenjs we can now say const balance button equals document dot getelement by id balance button. Thank you chatbt. And at the bottom we'll say balance button on click. We'll create a new function called get balance. So let's create that function. We'll do async function get balance like this. And we're going to do a lot of the same of what we did before.

So I'm going to do if type of winner does not equal defend blah blah blah all this stuff. I'm going to copy all of this. Paste it right here. Oh, and then be sure to close those parenthesis as well. We actually don't need the addresses. So, I'm going to get rid of that. But since we want to check the balance of a contract, we don't need a wallet for this. So, instead of doing a wallet client, we're going to say public client equals create public client like this. And then we're just going to say const balance equals await publicclient.get balance. And then we pass in the address which is going to be

the contract address. And then we can just say console.log format ether balance. So format ether is a function that helps make reading amounts easier. So up here we had this parse ether which turned one into 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8. And this format ether function is going to do pretty much the opposite. It's going to turn 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 into a one. And this comes from VM as well. Boom. Format ether like this. Now we can call get balance. We can see our node running here. And this is the transaction where

the coffee was actually bought. Oh, and we wanted it to be contract address spelled correctly. So now we can call get balance. And we do indeed see a one returned. Now you might see a zero here because of the way blocks work. And if you see a zero here, you could just easily, you know, pop open your accounts, maybe send 0.001 ETH to some random other address just to push a block forward. And then get bounce should return a one. If we buy some more coffee, we fund this with more. I call balance. Well, same thing. You might still see a one because you need to push a block forward. Uh, this is where some test functions like mine might be helpful, but I'm just going to go ahead

and send another random transaction to push a block forward. Now, if I call get balance, I get a two and so on and so forth. I could put two in here. Buy coffee. Confirm bunning with two transaction hash. And, uh, maybe I just want to push a block forward here. So, let's go ahead. We'll just send a tiny amount to some random address as well. Now I call get balance and we see it's indeed four. If you don't want to have to kind of do that bit that I just did, uh, what you can do is control C kill your blockchain rerun anvil- load statef fun.json

d-block time five. And this will mine a block every 5 seconds, but it'll start from that original state. So now what we can do is we can hit get balance and since I reset the blockchain it's now reset to zero. I call buffy one confirm transaction hash that means it goes through and so long as I wait 5 seconds another block will have mined. I can even kind of look in my node here. I can see okay transactions here a new block came here. This transaction was on block six. We're now on block seven. We're now on block eight. So if I call get balance guess there was a little bit of a delay there. So, I could do it again. Buy coffee. Confirm. Now, it should be five

+ 1, which should be six. I guess I have to wait a little bit for enough blocks to be mined. Maybe I should make the block time much faster. Maybe like 1 second or something. But eventually, I get the six amount here, which is very exciting. And almost accidentally, so let me go ahead and kill this. And remember, every time you kill the blockchain and restart it, if I go back to my UI, I hit get balance, it gets reset to zero, right? Because the only thing, the only state that's automatically loaded is the contract being deployed and our wallets having some money in it, right? So, every time we reset, we reset the whole state of our smart contract, which is good

because that's usually what we want to test anyways. So, let's go ahead and kill this so it doesn't keep running. And let's go to our readme. And whoa, we accidentally added a bunch of stuff on this list. We now have a button to buy coffee. We have a function on a we called a function on a smart contract. And we have a test blockchain that we can call. Oh, okay. Um, we're going through a lot of advanced stuff really quickly. So, let's just take a a second to go over a lot of the things that we just learned. Oh my goodness. Oh, we're almost done. All we need to do is add a withdraw. So, let's take a second and just kind of review everything that we went over because it's a lot. And

remember, if you get stuck, if you have questions, ask your AI, jump into the discussions, jump on Twitter, jump into Discord. There are tons of people learning all this fantastic stuff. Ah, you picked the best time to get into blockchain tech. So exciting. Anyways, let's go over everything we just talked about. Okay, so we learned how to connect our wallets to our website. This enables us to interact with a MetaMask from our website. We know that we can interact with it because we have this window. Ethereum object and this window.ethereum object gives us access to the blockchain node that is inside of the MetaMask. Once we connect our wallets, we can start calling functions

on different contracts. We played around with calling the fund function on a contract. To call the fund function, we set up a wallet client, which was a client to interact with a wallet, and we set up a public client. Before we called the fund function, we simulated calling the fund function, and then we actually called it. Now, the way we tested this was we ran a locally running blockchain called Anvil and we loaded it up with this FundMan Anvil that our smart contract developers were so gracious to already deploy the contract to at the address that they told us. This fake chain has access to a couple different accounts that we loaded into our

MetaMask so that we could actually interact with the website. We were able to buy some coffee by calling buy coffee, putting a one in the or however much amount. And if it gets stuck, just give it a little refresh here and confirm. And if we bought coffee again to just push a block forward, we hit get balance. We see the balance of the blockchain actually increase. We're able to get the balance of the blockchain because we added another function called get balance which all it did was call get balance on a specific contract address which returns the ETH balance of that address. And then we know if we were to hit control C and kill our

locally running anvil node, if I were to call get balance on the front end now or buy coffee, just nothing's going to happen cuz it's like, hey, uh, you're not connected. There's no blockchain running. I'm erroring out. Great. So, now is a great time to maybe take a break. I highly recommend taking breaks throughout this course and throughout every course. Your brain cannot absorb hours and hours and hours and hours of information in one sitting. It's best to take breaks and come back. I usually say if you go 30 minutes and take a 5-minute break, that's pretty good. Or if you go for 55 minutes and then take a 10, 15 minute break, that's pretty good, too.

But be sure to take breaks. Or maybe you go 30 minutes and be done for the day. Or maybe you go 55 minutes and be done for the day. Whatever pace works for you. So now's a great time to take a break. Go get some coffee. Maybe go to the gym. Let this information digest. And I'll see you in a bit. All right, welcome back. Let's keep going. So, we have one function left to do and then I want to show you converting all this to TypeScript and what the difference is working with TypeScript. All right, welcome back. We only have two things left to do. just add a withdraw function because right now we can deposit money to a contract but we can't withdraw it and then upgrade over to Typescript and show you

a lot of the differences between JavaScript and Typescript. So all right let's jump in. So withdraw. So let's go ahead. I killed everything. Let's rerun that node. Anvil load statef fun right here. And on our front end I'll do a little refresh. We'll connect. Get balance zero. Let's go ahead and fund it with one. Confirm. Get balance is still zero because of the block stuff. Let's fund it again. Okay, now it's one. So, it's now funded with one. It's technically founded with two, but it has some ETH in it. So, now we want to get that ETH out. And we can leave our terminal running and we can leave the blockchain in this state here. So, now that we've done the fund function, we just need to redo this for the withdraw

function. And this is once again where AI is actually incredibly helpful. One of my favorite prompts to do is something like this. Here is the code to call the fund function on a smart contract from my website. Can you please use this as a template to create a withdraw function that calls the withdraw function. It should send zero value because in here we have value where we send some value. And now we basically want to just do this exact same thing but call withdraw. And so I'll do three back ticks. I'll paste my

code in here. Three more back ticks. Hit enter. And let's see what it gives for me. Once again, if your ad gives you something great, go ahead and run with it. If it gives you something terrible, try to figure out why it gave you something terrible. And of course, you can always refer back to the actual code associated with this course. But I do want you to get into the habit of working with some type of AI because yeah, in your professional careers, you will be working with AIS. But again, what's important is you need to understand every single line of code associated with this. And I mean that. I want to draw your attention back to this for a second. The blockchain world is a little different than the web 2 world.

In the web 2 or the regular internet world, if your website that hosts kitten images gets hacked, the worst thing that's going to happen is maybe instead of showing cute kittens, you show, you know, weird images of Donald Trump or something. I don't know what whatever weird thing you want to show. Obviously, that's not great, but it's not the end of the world. If you get something wrong in your smart contract application, either in your Solidity code, but also in your frontends, you could actually lose millions or billions of dollars. So getting your code correct in the blockchain world, in the web 3 world, in the cryptocurrency world is incredibly important. And we never want to blindly copy paste code out of our AI. We want to make sure we know exactly what it

does. This $ 1.5 billion hack that happened just a month ago as of recording was not a smart contract vulnerability. It was a website. It was a front-end vulnerability. So, we want to be extra careful and pay extra close attention to whatever our AIS give us. So, if you want to go ahead and try to write this function out from scratch, I highly recommend you do that. If you want to get this from an AI, if you want to prompt an AI and have an AI do it, great. You can do that as well. But just make absolutely sure you understand what every line is doing. So I'm actually going to copy this. I'm going to paste it in here and we're going to go through it to see if it actually makes sense.

Okay, cool. Async function withdraw. That looks good. console.log withdrawing funds. Great. If type of windowthereum does not equal undefined. I think that's right. But let's go ahead. Let's even do like a little open the command pal and do a little split screen. We do split editor like this. And we could scroll down to fund. And now we can kind of compare these two functions. So console.log if type of window.ethereum. Oh, and if your windows look like this, can open up your command pallet and do toggle word wrap. And that'll get the words to kind of wrap around like what you see here. So if type of window.ethereum, okay, those look the same. We get a wallet client. We connect

wallets. We request addresses. Okay, that looks the same. We get the current chain. Great. We get a public client. We simulate the request on the withdraw. We simulate the request on the fund. Okay. So it says change to withdraw. Okay, good. So function name is withdraw. That looks right. Address looks right. AI looks right. Value parse ethers zero. So this is actually good. But we can actually delete this. And this is also really good for us to actually go through and delete that line. Right? So it says, hey, you can have this at zero.

Um, but it's just like, okay, well, why would we even clutter up our codebase with this line that we don't need? I'm just going to delete that. So, that's good. Okay, cool. We have a wallet client. Contract withdraw transaction. And okay, that looks good. All right. So, it looks like the code the AI gave us is pretty good. Let's go ahead and try to run with this. So, now that we have this withdraw function, let's do const withdraw button equals document.getelement. get element by ID withdraw button. Thank you, Chat GBT. Back over here, we're going to need to create a new button. ID equals withdraw button and we'll just say

withdraw here. And we'll have the button be here. So, we have a withdraw button at the bottom. We'll say withdraw button onclick on click equals withdraw. Okay, that looks good. Let's pop over to our front end now. Buy me a coffee. We do get balance. We see the balance is two. If I try to call withdraw now, we should see transaction pop up. Let me confirm withdraw transaction hash. Let's try to call get balance now. It still says two. Let's fund it with like 0.001 just to minor block here. Oh, and we got into an issue because that's not enough ETH.

Let's do 0.1. Now, let's call it Now, let's call get balance. And we see it has indeed been updated. So we successfully withdrew from the contract. Awesome. This is so exciting. And what's most important is the way we did it was we asked an AI to give us this function for us and it gave us the function. We walked through it line by line. We understood what every single line in here does. And that was the reason we felt comfortable to add it in here. And if we didn't feel comfortable, we could reference the docs. We could ask our AI to, hey, can you read? Can you tell me why you made the choices that you made?

There's a lot of different ways we can go ahead and verify that this is indeed correct. Okay, great. So now our app is pretty much complete. This is exactly where I wanted you to get. So we can now connect, you know, so long as our blockchain is running, we can get the balance, which is 0.1 right here. We can fund it by calling buy coffee which sends however much ETH we put in here. Right? So we see amount is one ETH. I'll confirm. We will do a little refresh. Get balance.

We have to wait for the blockchain to catch up whatever. Um but then we can withdraw everything which will withdraw the funds. Take the funds out once again you know when we call withdraw again. And now um get balance will return zero. And we can see the balance. So this is fantastic. Our application is working great and it's all in raw JavaScript. This is phenomenal. Now what I want to do now is I want to rewrite this whole thing in Typescript. Now historically this would have been a huge pain in the butt. But because we have AI nowadays this is much easier. So what is Typescript and why does Typescript

exist? Well, the TypeScript lang has a whole page dedicated to why TypeScript exists. But essentially, one of the issues with JavaScript is that languages without strong typing can get really hairy to read and can get really hairy to maintain as the projects get bigger and bigger. JavaScript, in my opinion, is kind of a flimsy language and oftent times bugs that you'll run into are bugs just because it's hard to read. TypeScript extends JavaScript. So, it's called a superset of JavaScript. It's essentially JavaScript plus some more stuff. And all it really does is add typing to the language. The issue with TypeScript though is that websites can't

read TypeScript, they read JavaScript. So, instead of indexJS, we would do index ts.ts. And there's a reason there's a method to my madness with these hyphens, I promise. But I can say like let you know what index ts.ts. With this ts I can now do my num of type number equals 1. Save this and I don't get any weird squiggly underlines because this is valid. I cannot do script src equals index-ts. You know the same way we did it with this JavaScript line because your browser will just kind of freak out. We can even try it. Ready? Watch this. TS save. Go back to our website,

refresh it. Failed to load module script. Expected a JavaScript module, but the server responded with a blah blah blah. Basically, it's like, hey, I expected JavaScript, and I got some other weird nonsense. It's cuz Typescript is not JavaScript. So, we'll undo, we'll resave, and that error goes away. So, Patrick, you're telling me we want to work with TypeScript, but Typescript doesn't even work with HTML. What the hell are you talking about then? Like, what is there to do? So here is what there is to do. When you write in Typescript, whenever you want to write your code in Typescript, you actually need to first compile it to JavaScript and then use that file. So for example, this like let my num number equals 1. We can actually compile this

file from TypeScript to JavaScript. So I'm going to stop this node. And now what I'm going to do is I'm going to install TypeScript as a dependency. So, I'm using PNPM. And if you don't have this installed, do PNPM-V version. Um, you can go to install PNPM and install this. If you have not, again, you shouldn't be using Windows. You should be using WSL. So, you could use one of these as well to install this. And once you have PMP installed, we can add TypeScript as a dependency. So now we're moving away from kind of this raw JavaScript into a little bit of Node.js territory. So when I do pmppm add

TypeScript, this is going to give me the TypeScript compiler so I can compile that. And it's going to do a couple other things. So let me let me run this and then let me explain what uh what's going on here. So this made a couple of files. Number one, this made this node modules folder. So this is a list of dependencies for a project. we install dependencies directly from the website. This node modules basically downloads them and puts it in this node modules folder instead. For doing like raw JavaScript and HTML, having a node modules folder isn't great for vanilla JavaScript, but that's like a whole other conversation. We also got this package.json. Now, this is a file that

we're going to get pretty used to working with. Uh, and in here it's just a JSON file with all these squiggly brackets, but we get this section called dependencies TypeScript. This means that our project now depends on TypeScript and it'll install all the stuff associated with TypeScript in this node modules folder. So now what we can do though because we have it installed, we can run PMTSC or this is the TypeScript compiler. And if I hit enter, it'll, you know, print out kind of the help command for this. So I can now run pm tsc index-ts. And this will compile index-ts to

index.js. So this was our original file, right? And we just compiled it with tscindex-ts.ts to index-ts.js. So what it did was it compiled this TypeScript to this JavaScript. And you can kind of see they're basically the same. Obviously this is valid JavaScript. This is valid TypeScript. And that's how it gets compiled. So if I went back into our TypeScript file and I did like let hello string equals hello and I were to compile this again. Now in our index-ts.js, JS we see that it gets compiled in here.

So if we want to use these TypeScript files in our index um in our HTML here instead what we would want to do is we would rewrite our index-js.js all in Typescript compile it to JavaScript and then in here use the compiled version. So it would be like index-ts.js. So hopefully that's a bit clear. Now what's great about AI is these days what you can do is you can copy this whole thing, dump it into AI and say rewrite this in TypeScript.

Three back ticks like that. Enter. And it'll do a pretty good job of rewriting this. Once again, my favorite at the moment is Claude 3.7. However, DeepS is free and should do a pretty good job. Let's see how it does. And once again, if it does a bad job, you can always come back to the codebase that I have in the GitHub repo, which will have what is correct essentially. Now, once again, as this is printing this out, exact same as with the JavaScript and we're working with AI, especially in the blockchain world, we do want to go through this line by line and make sure this is correct. So, I'm going to copy this, bring it over in my index slashts. I'm going to go ahead and paste this whole

thing in. And I can already see there's a couple of squiggly lines for a number of good reasons. Number one, with TypeScript, the import syntax is going to be a little bit different. So in our JavaScript, we imported like this, but in our TypeScript, we're going to import from our node modules. So we need to get this VM package. So we're going to do PN PM add VM like this. And this will install the VM package in our node modules as well. Next, it's mad about this Ethereum thing. It says property Ethereum does not exist on type of window and type of global. The reason is because we need to import another thing from VM that tells Typescript about this

window object. In Typescript, you can't just have random stuff kind of float about. You need to be very specific. So, we need to import a different window object. So, we're going to do import bm/window. Save that. And now, if you open up our command pallet and we do restart TypeScript server, those errors go away because this VM/window has all the window. For our TypeScript, you'll notice in here uh that we have some types now. So, we have as HTML button element. This is basically saying our connect button is of type HTML button element. Our wallet connect is of type wallet client which

is imported from VM. Our connect function returns a promise. It's an async function that returns a promise. The rest of the code is pretty much the same. The get current chain says the client is of type wallet connect. It also returns a promise. Here we have get balance which returns a promise as well. And that's pretty much it. Now if you want to take some uh now we could also rewrite constants in Typescript and in the GitHub repo associated with this. We did indeed do that. You don't have to if you don't want to, but now is a good time where you can do one of three things. Number one, prompt an AI to get this. If you don't feel like doing that, just grab the file in the repo

associated with this course, copy paste it in here, and try to understand what the difference between the TypeScript and the JavaScript is. Or write this from scratch yourself. Remember, repetition is the mother of skill. And I actually do think it's a good exercise for you to maybe just delete everything and say, "Okay, I'm going to try to build this myself. What do I need to do?" and then use AI or you know use the code that we're working with as context. So if you want to pause the course, pause the video, now's a great time to do that. Okay, welcome back. The final thing of course that we need to do is we need to compile this to JavaScript. So we would do pmppm tsc

index-ts. And this will compile all of this code in here. And it might take some time and it might actually error like what we see here. And we do indeed get an error because well we get this error because TypeScript is very fickle as well. And what we need to do is we need to give our TypeScript compiler a config and tell it how to compile all this code. Now for this crash course I'm just going to go ahead. I'm going to give you the TypeScript configs. AIS are great at helping you figure out TypeScript config. For now I'm just going to say go to the GitHub repo associated with this section. grab the tsconfig.json file, paste it in here, and these will be the settings for our TypeScript config. Now, when we rerun this compiler, it's going to read

directly from here. I forgot in my config, I told it JavaScript is no good. So, we do need to rewrite uh constants-js in Typescript, which I'm just going to make your life easier. I'm just going to say, you know, you can copy paste it from here as well. Uh it's basically the exact same thing. We just put the word export. Uh there's almost nothing different here. So, but we'll just do constants-ts.ts. Paste it in here. And now instead of importing from And now in our index-ts.ts, sorry, I know this is a bit confusing. We can just import from /constance-ts and then we can just restart the TS server and that little

squiggly line will go away. So the way we did our TypeScript config is we're basically only working with TypeScript files. So TypeScript is looking to import from TypeScript and instead of calling it coffee AI, I just called it AI in this file. Sorry. So we got to scroll down then and change this to AI and then we should be good. Okay. So now if we compile this TypeScript, it'll give us a new file or it'll error again. So now if we compile this, it'll actually error again because of a whole bunch of other reasons um that are not super important to go deep into. Um but you can actually see if you go to Typescript-TS.js, we now see this huge

huge mass of file and this is basically the JavaScript code that our TypeScript compiled to which is pretty cool. Now doing kind of these compilations can be a huge pain in the butt. Um, and I forgot about a couple of other things that uh doesn't really make sense for you to learn right now because it's not super important. You kind of work with an AI to do a lot of this. So instead of doing kind of all this hard compilation, what we're going to do is we're just going to use a bundler that's going to do all this for us. We're going to use a tool called Vit. So we'll do PMP add Vit, which will install this VIT thing.

And now what we can do is we can just do PMP vit. And this will do pretty much everything we want it to do. And then running pmppmvit. If we go back to our index.html and we change this to index.ts, what vit will do is it'll see this line and it'll go, "Oh, oh, this is TypeScript. TypeScript doesn't work in HTML." So, here's what I'll do. I'll compile this for you and I'll put the JavaScript in here myself cuz compiling the TypeScript is a pain in the butt. So what I could do now in here is I could do like a little console.log hi from this TypeScript file save it go back to the UI refresh and I

can see the high being output here. So VIT is a little bundler which helps compile the TypeScript from TypeScript to JavaScript and put it in our HTML for us. Now in the future all the tools we work with are going to do this for us and you're basically going to not know that that's even what's happening. But that's what's happening under the hood. The React and the next.js is compiling the TypeScript to JavaScript and then sticking it in the website here. And with that being said, we've now done everything that we want to do here. So, congratulations for getting this far. And let's do a a quick little refresher on everything that we just learned because we just learned a ton.

And I'm going to switch this back to JS just because it's a little simpler. I'm going stop and then restart. Great. And then I'm also going to do anvil--load state funme-anvil.json. Okay, cool. So, we created our very first website that interacts with a blockchain. Oh my god, this is so exciting. And we built it in raw JavaScript. We used AI to help us out. We did a little bit of vibe coding. And we even learned how to redo it in TypeScript. Although for this we kind of skipped the details a little but it's not super important because the tools we're going to use in the future are going to do all this for us. So, but essentially we created some very minimal very basic HTML and we added the logic

to all these buttons by creating this index js-js file which had all this logic for us. So, first we learned to connect to our blockchain wallet where if we go back let's go ahead let's select this. I'm going to disconnect. If I hit the connect button, my MetaMask pops up, says, "Hey, do you want to connect?" I can say, "Yeah, I sure do want to connect. I want to connect exclusively on the Anvil chain. Let's update connect." And now I am connected. And I can see little green circle in my MetaMask that shows that sure I am connected. We learned how to if we rightclick and hit inspect, read the console. If I hit get balance right now, it's going to show zero because we have

not interacted with the contract. But we learn to read data from a contract, right? We have get balance. We have our get balance function which connects to a public client calls the get balance function which returns the balance and then it prints the balance out to the console. A little bit more interestingly, we learned how to call a function. We hit the buy coffee button. We actually get to call the fund function on ours here. If I confirm, we get a little hash. I'm going to call it again just to push the blocks forward.

And now hit get balance. We do indeed see a balance for the address here for the smart contract. We were able to call this fund function. We created a wallet client to get our connected address. Then we created a public client to first simulate calling the fund function. And then after we simulated calling the fund function and passing a little bit of ETH with it, we actually called the fund function and then printed out the transaction hash associated with it. And then finally, we can call the withdraw function. And I'm just going to call it twice to uh basically mine an extra block. Now, if I call get balance, I see zero. I see we pulled all the funds out

of the contract. We learned a little bit more about working with some of these AIs. The one we worked with in this video was DeepSeek because it's free. However, like I said, Claude 3.7 as of recording is Patrick's favorite. And if you want to use Cursor, GitHub Copilot, whatever you want to use is Groovy. We then finally tinkered around with TypeScript. We learned that TypeScript does not work in HTML. And what a lot of the tooling that we're going to use in the future is going to do is it's going to take our TypeScript. It's going to compile it to JavaScript and it's going to stick it in our HTML for us because dependencies and stuff are kind of weird

when you do that and we don't want to have to deal with that ourselves. So, you have done fantastically to get this far. And the next thing I'll say is I highly recommend you learn how to push all this code up to GitHub and you start building your profile. Building your GitHub profile is one of the ways you're going to prove yourself as a developer, as a security researcher, as somebody technical in this field. So, I highly recommend you start pushing to GitHub. And it's not something I'm going to go over with you in this course. However, AI once again are very good at working with GitHub. So now's a great time to take a break, go for a walk, get some coffee, get some ice cream, because

we're about to take this to a whole new level in our next section. So take that break and I'll see you soon. Now, actually before we finish up here, we have one more challenge for you. So your first challenge was pretty simple. It's just to write down why you want to take this course. Second challenge, a little bit harder. create a button that console.logs out the response of get address to amount funded. So in the contract that's deployed to that anvil chain, there's this get address to amount funded. Every time you call buy coffee and you send some value to the smart contract, it

keeps track of how much you've sent. So if you have a few different addresses funding, you can tell how much each different address actually fund the contract. I want you to create a button that will read this get address to amountfunded function from the blockchain and console.log it out to your terminal. So it'll be just another button here. You'll click it and some stuff will show up out here and that's going to be your challenge. It's going to be a read only. So, that's going to be your challenge here. It's going to be a little bit different because you're not going to be writing a It's going to be a little bit different than what we've did because instead of writing to the blockchain, we're actually reading

from the blockchain. So, you might have to read some documentation. You might have to work with the AI a little bit, but that's going to be your challenge here. So, pause the course, take some time to do that, be sure to work with AI, be sure to work with the docs, and that is your challenge. All right, welcome back. Welcome to section two of the AI vibe coding full stack web 3 developer crash course where once again just wanted to remind you we need to get things correct here because if we

don't we run the risk of getting hacked for billions of dollars and we don't really want to do that. We're going to be leveling up a lot here. There is a lot we're going to be learning and we're going to be building a site that actually exists today. tender.com is an actual website you can visit where we can go ahead, we can connect our wallet, connect to MetaMask connect, and we can actually send transactions and we can actually airdrop a ton of ERC20 tokens to different people. There's a safe mode and an unsafe mode on this site. We have some different supported chains including Ethereum, Optimism, Arbitum, and of course, ZK Sync. And we have an anvil connection as well for testing. Now, in

our MetaMask, if you do not have ZK sync, one of the things you're going to want to do is scroll down. There will probably be a button to add ZKYNC. If you scroll down in your MetaMask, MetaMask actually comes with ZKYNC builtin. You can just click add and approve. Add it to your account and you'll be on ZKY here. What we can do is we could look up like the USDC address on ZK sync on the ZKYNC tokens explorer. We could grab the ZKYNC address here.

Paste it in. And what we can do is we can type in a list of recipients to receive this token and a list of amounts. So maybe I'll say I want this person to receive 100 or this much way. And we'll switch from Anvil to ZK sync. We'll confirm. And what our UI will do is it'll tell us at the bottom, okay, we're going to be using the USDC contract with this much way, which equals to one USDC or $1. And then you'll click this button, which will call a function to approve the contract, and then a function to actually send the tokens to this list of addresses. If you

have multiple addresses and multiple amounts, the bottom here will add them up and you will airdrop the amounts to each recipient here. This is the tool that the Code Hawks team actually uses to airdrop tokens to the winners of the different competitive audits. So, you're getting a little bit of a sneak peek into some of the behind the scenes of how Code Hawks and the Ciphern team build different tools both for internal use and of course for external use. This was the codebase here. The codebase for this is on this Tender GitHub repo. The smart contracts for this are in this Tender GitHub repo. And if you go into SRC,

uh some of the contracts are written in this language called Huff, which is this like crazy gas optimized language. Uh which is why this was created in the first place. This is the most gas efficient airdrop contract on Earth because it's built in this very low-level language called Huff. So now if you go to the code associated with this, this is going to be the final project, the final code of what this is going to look like. And this is a lot bigger than what we did last time. This is a Nex.js or React project. Nex.js and React are known as JavaScript frameworks. They make doing a lot of that kind of complicated stuff that we got to towards the end like compiling

the TypeScript a lot easier for us. and they make it easier for us to build more complicated, more extendable applications. So, because of that, there's going to be a lot more code. In this, we're going to be working with a lot of different projects like Rainbow Kit. We're going to be working with WagMe, which is one of the most popular tools for creating these React apps and so much more. This site we're going to be building completely statically, and we're going to be deploying it and hosting it using a platform called Fleek. Fleek has a lot of fantastic tools and is one of the sponsors of this full stack curriculum and I'm super excited to deploy and if you want you can even deploy for free this

application to Fleek. So with that all being said this is what we're building. This is what we're going to make. Let's go ahead. Let's get cracking. All right. So here we are back in our codebase in our VS Code. We're going to go ahead and get started setting up this new project. If you haven't already turned off your live server from the last project, now is a great time to do so. I'm going to CD down a directory. Do pwd. Make sure I'm where I want to be. And now create a new directory called ts t sender ui cu for which stands for typescript tender UI

siphon updraft. I'll hit enter and I'll do code ts tab t sender UI cipher updraft or once again we can do file open to open this up in a new window for us to focus on. We can go ahead and click the final code here. Scroll down in here to click on this to see the smart contract code as well if we would like to. So let's talk about what are we going to do here. Let's create a new file readme.md. Enter. What are we building? Well, a whole bunch of stuff. But this little image here kind of gives us an idea of what our website needs to do.

So, let's let's write it out. So, number one, uh, and if we go to the actual codebase, the smart contracts are here. We can go to the T sender. If you go to SRC uh actually don't go to protocol go to reference you can see kind of a solidity written implementation which is really really really simple for this smart contract the main function that we need to implement is just this one single function airdrop ERC20 it takes as inputs an ERC20 token that's going to be airdropping a list of recipients a list of amounts and a total amount So, an ERC20 token. Now, for those of you who are unfamiliar with what an

ERC20 token is in the GitHub repo associated with this uh section, there's a video called create your own blockchain ERC20 token where you only got to watch like the first like 2 3 minutes to see what an ERC20 token even is. There's a link to this in the GitHub repo associated with this course if you're unfamiliar with a token. The quick explainer is essentially an ERC20 token is a contract deployed to a blockchain that represents a token. That's pretty much it. So yes, Ethereum is like a cryptocurrency in that we use to pay for gas, but you can also deploy tokens to the Ethereum chain as well. So each one of these chains have their own

contract address because a token is essentially just a lump of code. So we need to pass which token we want to airdrop to people or send to multiple people, a list of recipients and then a list of amounts. So this token address is going to be the ERC20 token. The recipients are going to be the list of addresses to get the token and uh the list of amounts. So, and the way that the recipients and the mounts kind of link up, like if this is the, you know, address A, address B, address C, if this is like the list of recipients, and then we say like 100, 200, 300, and this is

the list of amounts, address A would get 100, address B would get 200, and address C would get 300. So this is index zero. This is index zero. Index one, index one, index two, index two. That's how you decide which recipient gets which amount. And then obviously the total amount, which should be the sum of the amounts here. So this is the function that we're going to have to implement. So right at the top, we can just say one implement this function. But before we even do that, we need to create a basic react.js JS application and it should be a static application. So as I mentioned before

the code for this there's a lot of code for this. The reason is because this is going to be our first Nex.js application which is a little bit more complicated even for something as simple as this. So we're going to have to create some boilerplate code and then we can implement this. Uh don't and we're not going to worry about the static part for now. Once we have all this up and we've tested it on our local chain, we're going to deploy to Fleek. You at the end of this can have your own tender.com, you know, on a different domain obviously and deploy to IPFS as well. Yes, at the end of this you can deploy a contract and when you do so you should go to Twitter or X or whatever and be like, "Oh my god, I deployed my

first full stack application. Here it is." Obviously, you know, you're going to have some readable text here instead of what I just did. So, but we're incredibly exciting because not only are you going to be able to interact with real contracts, but you are actually going to deploy a website yourself. And like I said, anytime you're looking to build an application, you need to have a badass front end. You need to have a badass website to interact with that smart contract. So, we are going to do it here. And I'm so excited for you to do it here. And you should be super proud and you should be super excited and super proud to get to the end of this section because if you've never deployed a website before, we're about to do it. If you've never deployed a

website that interacts with blockchain, we're about to do it. You will have a website done, a badass website done by the end of this. So, I want you to celebrate once we get to the end of this because this is going to be so exciting. Okay, so these are kind of the basic things that we have to do, but we're going to take it step by step. Let's get started just learning about what the heck this React Nex.js JS nonsense is and let's go from there. All right, let's go ahead and get cracking. Now, we're first going to create a basic React/Next.js application. Now, I'm not going to spend a ton of time explaining what this is and what this means and going deep into it because there's so much information out there already. And

because the JavaScript world moves so fast, oftent times these frameworks update and change and this and that and the other thing. At Siphon, we're less interested in teaching JavaScript. We just want to teach blockchain technology. There's plenty of places out there to teach you JavaScript and Nex.js and React. Not a lot of places to teach blockchain. AIS also are phenomenal at Nex.js. They're phenomenal at working with the tooling that we're going to be working with here. So, I'm going to skimp over some React details, some Next.js details, but I'll tell you when you should, you know, maybe pause if you don't understand something. Maybe go to your AI, ask some questions, and of course, we'll be doing a little bit of

AI question asking ourselves. React is the most popular JavaScript framework for building websites out there. It was created by the Facebook team and pretty much everyone uses it. Nex.js JS is built on top of React and is created by the Verscell team which is one of the most popular website hosting companies out there. So by you learning React and you learn JS you are learning the most popular tool out there which comes with the benefit of being highly used by a lot of AI. Now to get started building a Nex.js application we're going to open up our terminal and since we already have PNPM installed you can follow the Nex.js JS documentation to find the command or we can just run

pnpm create next app at latest and this will go ahead and start creating our project. What's your project name? Well, it's going to be ts sender or t sender. Would you like it to be script? Yes, we're going to use typescript. No, we don't want to use eslint. And then you can kind of do like left or right to choose tailwind CSS. This is going to be uh so TypeScript, we're going to use TypeScript in instead of JavaScript. This is something called linting. Don't worry about that for now. Tailwind CSS. This is going to make styling our HTML much easier. We're going to hit yes.

Would you like your code inside an SRC directory? SRC stands for source. We're going to say yes. Would you like to use app router? We're going to select yes. I'll show you what that means in a bit. Would you like to use Turboac? We're going to say yes. We don't need to worry about that at all. Would you like to customize the import aliases by default? I actually prefer this, so I'm going to say yes. What import alias would you like configured? I'm just going to hit enter. And it's going to go ahead and install and set all of these up for us. If you have a custom setup that you prefer, obviously feel free to do whatever you want to do. Now, I'm going to hit clear. If I open up my terminal, I see it created another folder. So, I can now cd into tsender UI. If I do pwd,

I have ts tender UI. TS uh tender UI. If you want to leave it like that, that's fine. It's going to kind of annoy me. So, I'm actually going to shiftclick all of these and paste them here. And it's going to take a little time to copy. And it did. And now I should be able to delete this. Let's just make sure all the the files got moved over. Looks like they did. Okay. So, now I can delete this one. Move to trash. Great. And now that we've got this set up here, we can look in our package.json and we can see we have this set of scripts here. Now these scripts are basically little shortcuts for us to run some command. Now this next.js

project comes with kind of like a boilerplate project for us. So if I pull up the terminal, I do pmppm rundev. This will start up a little server for us. And I can copy this. I can paste it into my browser. And I can see this is the little website that our application made for us. And now I'm going to come back here, Ctrl + C, clear it out, and delete everything there. So let's just kind of briefly walk through the files that are in here so that we have a good idea of what we're working with. So TS config, we worked a little bit with this in the last section. This

is basically going to be our typescript config. So react and nex.js whenever we run this pmppm rundev it's going to essentially compile all of our typescript into javascript and it's going to use this config to do the compilation. We have a readme obviously we have a readme copy which the nextjs application created. Um I'm actually just going to see uh delete this. We give this post css.config.mmjs. This helps our Tailwind CSS which again helps with formatting of stuff. This is a lock file which tells us specific versions of different dependencies uh that we're going to use.

This is our package.json. This has all of our dependencies with not the super specific versions along with some scripts and some details about our project. We have a next config. This is the nextjs config. So this is the so when we compile TypeScript this compiles this tells us how to compile the TypeScript and when we run our nextjs project this tells us how to run the next.js JS project. Uh, ignore this file. Don't do anything with this. This is the getit ignore. You can ask your AIS about this one. Then we have src/app. Basically 90% of the code we're going to be creating is in this src folder. Uh, we have favicon which is

just like a little logo. Globals.css. This is like some formatting. So it gives colors and um when to use certain colors given different, you know, setups. We have our layout.tsx. This is going to be the layout of our entire app. You can almost think of this layout.tsx as the main function of our website. And then we have page.tsx which at the moment is our homepage. So if I have pmppm rundev running and I'm going to minimize it, not delete it, just minimize it. Right?

I'm going to hit this X. I'm not going to hit the trash can. So it's still running and I can refresh and it's still running here. I could look for, you know, get started by editing src app pages tsx. Could look for this. I'll do command shift F or just hit this little search bar over here. Paste this in. Let's look for get started by editing. And aha, you see that that is on this page. So, what's interesting about a lot of these files is that these are tsx files. And uh you know, let's say instead of get started by editing, I wrote hello. I saved it. I go back to our front end. Now it just says hello.

What's interesting about these files is that they're in tsx instead of instead of ts. So you might see that and be like, huh, what's the difference between tsx andts? TS is Typescript. So what's TSX? Well, this is where we can add Ask our AI. Sots is a regular TypeScript file. TSX is a TypeScript file that includes JSX or React components. So in these TSX or JSX files, we're allowed to create these things called React components.

You'll notice that like in this export default function home, we almost have like HTML and JavaScript. So, ReactJS allows you to create these React components and they're essentially these different functions which are a weird combination of HTML and JavaScript. And it's one of the reasons why React became so popular because essentially, you know, back in our raw, you know, in our raw HTML and JavaScript project that we did recently, we had our HTML in one section, we had our JavaScript in another section, and we combined them at the end. In these JSX or TSX files, you can kind of combine them even more. And it's pretty

interesting. Uh you'll see as we play with it more and more, you know what it looks like. But essentially um there's a lot going on here for us to start with. So we're going to cut everything down. So I'm just going to say, okay, delete all of this. Goodbye. Goodbye all this. See you. And I'm just going to do div. Hi. Hit save. Now if we go to the front end, let's look for this. Now it just says hi. Great layout. We're going to change. We're going to delete all of this here. We don't want any fonts. We're just going to make this super super minimal. Instead of create next app, we're going to say tender, no description. We're going to get rid of all this here. And refresh. And that looks good. So cool. Much more minimal

layout. Super minimal page. We can even get rid of this top one. Globals.cs can say the same. But okay. So this is what your layout.tsx should look like. And this is what your page.tsx tsx should look like. And this is where we're going to start from. Now, if you want to take some time, pause the video, pause the course, and start playing with this and start trying to build this yourself, I highly encourage you to do so. But if you want to code along with me, let's do it. We are, like I said, we are going to be using AI to help us out a lot here. So, great. So now we have a super minimal React site that does pretty much nothing other than say hi. Let's go back to our readme. What are we supposed to be

doing? Create a basic ReactJS application. Uh I guess I guess we did that. I guess we did that. Great job. Um so next is we want to implement this Airdrop ERC20. Now obviously this comes with a ton of stuff. We first off need to connect our wallets. So before we even implement this, let's connect our wallet with a nicer connect application. So let's first let's walk before we can run. Let's connect our wallet. So back in our HTML TS coffee, we had this connect button and it wasn't very good.

Right? If I pull this back up here, when we connected, it would say connected, but it would still be a button. If I refresh the page, it would no longer say connected. But then I would hit connect and it would be connected. But then if I disconnect, it would still say connected. But if I hit connected, then it would show up and I would connect and then it would be connected. And it it just wasn't quite right. Right. Remember, right, I just refreshed again. It says it's not connected, but it clearly is connected. So, we want to have a much better, nicer interface for users to connect their wallets to our application because what we currently have going because the way we did it

kind of stunk. So, what we could do to do this and let me just uh put a couple notes here. So we could write a ton of logic to to store some local cookies on the device to say okay yes this person connected with this wallet and on this address and remember it and if they refresh hold this and if they disconnect you know remove the cookie and you know we could add all this logic ourselves. we could just use a package that already has all this logic. And that's what we're going to do instead because we want to work incredibly hard to be incredibly lazy and I want to make this a little bit nicer than what you see here. Right? You go to a we open the app. One thing that they have that's

really nice is they have this header up at the top with this connect wallet. If I connect with MetaMask here, MetaMask pops up. I can connect and they give me this little this fun little address thing here. Oh, it says I'm connected with this address and then I can disconnect. I can reconnect. Ah, wow. That is so nice. If I refresh, it remembers that I'm still here. I want that. How do I get something like that? Well, there are a lot of different wallet libraries out there that automatically do a lot of this for us.

And the one that we're going to be using is this library called Rainbow Kit. Uh why are we using Rainbow Kit? Well, uh quite frankly, it's because I liked it and because it works great with this tool called WagMe, which is what we're going to be using. So, for those two reasons, it works well with WagMe, which is a tool we're going to be using. And I just kind of like it. That's why we're doing it. So, we're going to go ahead. We're going to copy this line at rainbow.meitus. And this is at rainbowit.com. or you know use whatever is in the GitHub repo assisted with this course and we're going to stop our website here and we're going to do pmppm add at rainbow.mme/rakit at latest. Now as of

recording we're using version 2.2.4. So if you want to use the exact version we're using in this video you can do pmppm add at rainbow.mmerakit at2.2.4 before like this instead of doing at latest. So that's how you can get a very specific version here. So now that we have this installed, we can go ahead and actually get set up with creating a header which has this. We can go ahead and get started creating this wallet. So if we go to the docs, it looks like we actually have to install a couple of other components as well. These ones right here. So I'm going to go ahead and copy those and I'm going to install those as well. pmppm add wagme vm and

tan stack. So we're going to add those as well and those will show up in our dependencies here. Then if you want to pause the video, follow along with the docs, feel free to do so or you can kind of follow along with me what we're going to be doing here. You can follow along with the docs, but I prefer you just follow along with me here uh because I'm going to be doing it a little bit differently than what the docs do. So to work with Rainbow Kit, you want to come up with a config first. And uh it doesn't look like they have a ah okay. So they have a little bit of information on the config here, but we're going to create our own config. So in the src folder, not in the app folder, create a new file called rainbow kit config.tsx.

And this is going to have the code to tell our website what kind of chains we can connect to, how to connect to our site. So first at the top, we want to say use client. This tells a React component that we want this stuff to run on the client side, aka in the browser, which is what we want. And we're going to do a little bit of boiler plate here. So, we're going to do import get default config from at rainbow-me/raboc kit. And then we're going to import our set of chains that we want to use. For us, I'm just going to say anvil and zk sync. We'll say from wagme/chains and then we'll say export

default get default. And this will be our config for our wallets. So, these will be the allowed chains that application will allow. Uh, and we're going to say our app name. It's going to be T sender. We're going to have to have a project ID, which I'm going to leave blank for now. Uh, you'll see why in a minute. And then we're going to say the chains, which for us is just going to be anvil and zinc. And then SSR is going to be false. SSR stands for server side rendering. At the moment, we're going to try to make this a static site. Now, what is project ID? So, according to the docs for this, every DAP that relies on wallet connect

now needs to attain a project ID from wallet connect cloud. This is absolutely free and takes a few minutes. So, with most applications, let's go back to a for example, and let me disconnect my wallet here. Whenever you connect a wallet, you'll pretty much always have, you know, MetaMask or Rabby or some type of software wallet like Brave or Coinbase. But if you go to other wallets, you'll also get this little wallet connect QR code that you can either scan or copy. So, there's lots of different types of wallets out there that also want to connect to these different apps, but maybe they don't have a native integration, right?

MetaMask, Brave, Coinbase, these all have native integrations. they can just kind of automatically connect to a but maybe you have like a ledger or a treasure or some other type of wallet that this application doesn't support. Basically for every other type of applications there's this company called wallet connect which will essentially facilitate a connection between your wallet and the website. Now for a website to use wallet connect you have to sign up for a wallet connect project ID. Now, you can 100% not use Wallet Connect, but for the most part, it just probably just makes sense for you to use Wallet Connect. It doesn't hurt. So, what we can do is we want to go over to Wallet Connect, and we want to uh sign

up. So, I'm going to I'm going to log in with an account here. Going to sign in with my wallet. Message looks fine to me to sign. I'm on a Oh, I'm on Ethereum mainet, which is kind of spooky. Maybe you want to log in with your email and username if you're not how to sign stuff. I've gone ahead and logged in. And now what you can do is your project will have a little project ID that we're going to go ahead and copy. And what we're going to do is we're going to create a new file called env.local. And this is where we're going to store some kind of sensitive but not super sensitive credentials such as our project ID. So I'm going to paste my

project ID in here. And now I can say next public wallet connect project ID equals paste this right here. So if I'm going too fast for you, feel free to pause and do that. And having this project ID will give us access to this wallet connect modal so that people with different wallets that our website doesn't support can still connect to our website. So, so for project ID I'm going to say to get this environment variable I'm going to say process.env next public wallet connect project ID. I'm going to save it and I'm going to put a little exclamation mark here which means that yes, this 100% will exist. So there's a couple steps to

getting this header in. This is the first step. We create our config here. Awesome. Okay, what's next? And at the end of this, we're going to have a nice little modal like this. But this is step one of our app here. We're going to create a new file called providers X. Now, this is going to be a list of tools that are going to wrap around our entire application. And one of the tools that's going to wrap our entire application is going to be this Rainbow Kit config. Why do we want to wrap it around our entire application? Well, we want everything inside of our application to know about our wallet, to be able to access our wallet. So, we're going to create this page called providers where we're going

to provide a lot of stuff about wallets. So, I'm going to say use client because I want something to be on the client side as well. And we're going to say export function providers. We'll say props children react node. If this doesn't make sense to you, that's totally okay. Just ignore it for now. We're going to say import type react node from React this. And in here we're going to say return low parenthesy rainbow kit provider. And we need to import that as well. So we're going to import config from at / rainbow kit. So this little at sign means inside of our src folder go

to rainbow kit config. And since we imported this as the default, we can just call this config. And we're also going to import the WagMe provider from WagMe, which is again something that we haven't used yet, but I promise it'll make a lot of sense soon. And in here, we're going to say WagMe provider config equals config like this. We'll put this in here. We're going to put props. In here, then we're going to do slashwagu provider. Oh, and sorry. We need to do import Rainbow Kit provider from at rainbow

meocit. We need to do this. And cool. So, what is this page going to do? Uh, I know this is a little confusing. Let me explain. We set up our config here, which is going to tell our header, our wallet, what chains we can connect to. Now in this providers, this providers component is essentially going to wrap around everything in our entire application. This WagMe tool is this thing that we're going to use to interact with the blockchain. So when we did a raw HTML and JS, we use this thing called VM. Wagme is built on top of VM.

And we want WagMe to be kind of encompassing everything that we're doing. So you see that inside of WagMe is the Rainbow Kit provider. And inside of the Rainbow Kit provider is this thing called props. You can kind of think of this as like all website code will be in here. So all website code is going to be essentially inside of this WagMe provider if you will. So now that we have this providers thing which has kind of the WagMe and the Rainbow Kit provider, we can finally go back to the layout which you can like I said you can kind of think of this as our home base.

And we have export default function root layout. Um, I don't love the way that this is set up. So, I'm going to delete all this. And I'm going to instead say props children react node like this. And I'm going to import React node from React. And this will be props. And now I'm going to do import providers from dot /providers like this. And I'm going to do providers. Providers. So what do we do just now?

Well, when we run our website, essentially we go to this layout.tsx page, right? And I can put like hey from layout. And now if we run this, we refresh our app. Uh, oops. No query client set. Use query client provider to set one. Oh, I forgot. But we actually have to also in our providers add a query client provider. So we'll do import and you get a lot of this from Wagme which uh I probably should have shown you first but that's okay. Um we're going to do import query client query client provider from tanstack query like this. Just roll with

me for now. It'll make sense. And here we're going to say const query client equals ustate. That means we need to install ustate. Just kind of blindly follow along with me for now. I promise we'll explain in a second. Importate from uh react. Okay. New query client. And then finally inside of Wagme, we're going to do query client provider. Client equals queryclient back slashqueryclient provider. Now let's kill and rerun. go to our front end, refresh the page, and it's now working in my layout.tsx. If I wanted to, I could say hi from layout, and we would

see hi from layout in here. Okay, so everything flows through this layout.tsx. So everything in our application is going to be wrapped around this set of providers cuz in providers we're basically saying everything is wrapped around these three things. The Wagi provider, query client provider, and Rainbow Kit provider. Why, Patrick? Why the heck did we do all that work? It doesn't look any different. Well, like I said, it'll be helpful later for when we actually go use WagMe for when we go to read and write to and from the blockchain. But for now, it looks like uh a huge waste of time. But it's okay because all of that work is

going to be make a lot of sense soon. So in here, we have these Rainbow Kit providers. And what we can do is we can now that we have all of this set up, all this provider stuff set up. We can add a connect button that comes directly from Rainbow Kit. So we have the Rainbow Kit provider. We can do comma connect button and take this and go connect button inside of here. And this will give us that connect button from our last project but with a lot more bells and whistles baked in. So now if I go to the front end, it says wrong network and wrong network detected switch or

disconnect. Uh and all the formatting is messed up. So we got to go fix the formatting somewhere. and uh your providers file works fine. You need to add the rainbow kit styles. So we'll do import at rainbow me/ rainbow kit slashstyles.css. And that'll apply all the styles to it. And now if we refresh, we get a much nicer looking connect button here where I can connect. And whoa, nice little popup will show up saying connect to wallet. What is a wallet? MetaMask pops up. We can choose our account. I'm going to choose my

account one. And we can choose our network. I'm going to just go ahead and hit update and connect. And what'll happen is if you hit this little dropdown, it'll make you choose between wallets that are actually supported. So, right now, I have set it up. Right now, I've set it up that only Anvil and ZK Sync are chains that work. It's causing a little bit of errors because my Anvil instance isn't up. So my MetaMask is like, uh, what's going on? But if I switch to something like Ethereum, it'll say wrong network. Please choose a network this application works with.

I'll hit CK sync. Okay, connect. And we can see we are connected here. And because in our config, I told you I promised I told you this was going to make sense. We only have Anvil and ZK sync in here. Those are the only two options we get on our front end. Mainet to from to anvil, Ethereum to Anvil, ZKYNC, and mainet. And we added mainet to this list here. Saved. We go back to our site. Select this dropown. We now have Ethereum as an option. And if we click it, our wallet will pop up. And ask us to move over to Ethereum. So I can click it again. Move back to ZK Sync. Go over to ZKYNC. And it's now

happy. Getting a little bit of issues with this connect button because of where this is. Let's actually take this connect button out of the providers. Let's go to page for now. We'll put it in here. And then so that means we're going to take this this line. Paste it in here. Get rid of this. Now it's refresh. Now let's switch to Ethereum. So now on our app, we'll go ahead. We'll connect our wallet. Oops, that's super zoomed in. Oh, connect to MetaMask where we'll go ahead. We'll connect here. Once you start from disconnected, put that earlier. Once start from disconnected, now you can see I'm on the Ethereum network. I'm on the Ethereum network on the app. We'll change from Ethereum to ZK sync. And since I've already approved

for ZK Sync, we're now on ZK Sync and we can switch to any network we want. Okay, excellent. Let's keep going. So, let me just kind of back up and explain what we did here because we did a lot of stuff and I told you just kind of roll with me, but let me let me have it all make sense. So, the way that this providers t so you can kind of think of everything going through our layout.tsx file first. So when you kind of boot when you boot up the page when we when we start our application here you're going to almost think of everything getting started from this root layout or this layout.tsx page and this props. You can basically and this

props.child piece here you can basically think of this being any page that we have show up on our app. So for example page.tsx this is what's going to show up on our app. And you can almost think of kind of taking copying this thing and pasting it here. And that's kind of how the app works. Now, the reason we want to wrap everything in this providers thing is because every single page that we're going to deal with is going to want to interact with providers or something at some point. So, we're going to want to interact with WagMe, which again, we'll show you that soon. We're going to want to interact with the query client provider, which again, we'll show you soon. But especially, we're going to want to interact with the Rainbow Kit provider because the Rainbow Kit provider is this thing that gives us

access to all these nice little features and connecting our wallet and disconnecting our wallet and such. So, in our layout tsx page, we wrap every single page that we render inside of our providers here. So, we can give access to every single page, at least the rainbow kit, so that every single page can have access to a wallet. If we didn't do this wrapping thing, then on every single page, we'd have to do like, you know, import, what is in here? We'd have to do import rainbow kit provider, import the connect button, import rainbow styles on every single page. And that's super redundant, and we don't want to have to do that. So, having this set up makes it so that we don't have to write as much code. Our code can be much

more maintainable, much smaller. And that's how you can kind of think about it. In order to pass objects to tags inside of tags, you kind of have to wrap them like this. So, if that doesn't make a ton of sense, please consult your AI companions or if they are not being helpful, ask questions in the GitHub discussions. All right, cool. Let's keep going. Now, before we continue on, if we go to tsender.com, we see that there's kind of a nice little header here. Now, we want to have the same functionality so we can separate this header from all the stuff that renders in the page because if we have different pages, we still want the header to render and we don't want to

have to retype all this code for doing the header code every single time. So, we're going to create our first component inside of our folder. So, a component is going to be like a reusable chunk of React code. Basically, that is just this header chunk. And anytime a page gets created, it just calls the code for this header chunk that we're going to create here. So in our src folder, we're going to create a new folder called components. And components are a really well-known, well doumented concept in full stack programming. And again, if you want to learn more about components, go to the next.js docs, ask your AI, go to the uh discussions, etc.

But in here, we're going to create a new file header.ts. tsx and this header.tsx is going to contain all the code that we want to have this header contain like our connect button everything. What's really nice is guys are great at creating headers. So if we want we could even just go to our AI again my favorite at the time is claude but deepseek works great too. You can even turn on the deep think for a uh here. And here's what we're going to say. I'm going to copy all this code here and I'm going to say here's my connect button for connecting my web3 app to the blockchain. And I'm going to have these three back ticks here. Going to put all the code in here.

Then shift enter. I'm going to say then I'm going to say can you turn this into reusable header component with GitHub link/button, right? Cuz that's what we see here. A title called tender connect button on the right side. So what's nice is we're already giving DeepSeek the code to use this connect button. So it doesn't have to use some random library. If we want to be even more specific, we can then say we can say, can you only use the following imports? We have our connect button import. We have this GitHub icon from this React icons package, which um is pretty pretty well known. So, we can get this little GitHub icon. We do have to install this if we want to use it though. So, I'm actually going to kill this. We do

pmpn add. And this is called React icons. React icons. Let's look this up. React icons. So, pmppm add react icons. Enter. Okay. Clear. Then go back to DeepSeek and next image. This comes built in with Nex.js. Let's go ahead and hit enter. And let's see what DeepS gives us. So, notice some important things. We didn't tell our AI that we're using Next.js, but it should be able to figure out that we're at least using React based off of this code here, right? Because this code is clearly a React or a JSX or a TSX component. We probably should have told it that this is TypeScript, right? Cuz there's no

typing in here, but we do want to be using TypeScript and there's no typing here. So, we probably should have told it that we want TypeScript. You can kind of see it's thinking here, which is great. And it actually it actually even says right here, let's plan the JSX structure. So, it did figure out this is JSX. It didn't figure out that it was TSX because there was no indication here that this was TypeScript. And when we're prompting our AIS, this is something important to know. You know, how much information are we giving the AI so that it's able to deduce what we're doing? We didn't give it any information here that it could have used to deduce that we're using TypeScript. You can almost think of this AI is like a human being. A human being would have seen this and said, "Okay, that's JSX. I'm going to

use JSX." And let's see what it gives us at the bottom here. Okay, so here's this header component. We're going to copy it and we're going to paste it into this header.tsx. I don't see any big issues here. We have your username. So, we're do change this to siphon t sender. That looks good to me. And then to use in your page component, do it like this. Okay, great. So, let's go back to our page.tsx. And instead of this, we're going to do import header from at /components / header. And then instead of connect button, we would do header here. Right like that. And let's see how it did. PMP rundev. Do a little refresh

here. And that looks pretty good. A little GitHub icon here. Tender. A little bar. This is great. If you want to add more styling to this, you can. But this is a pretty good example of our AI giving us some pretty good code here, right? This header component is relatively minimal. And what's important again, as I've said many times, anytime your AI gives you something, you want to make sure you understand everything that's going on. All of these class names here, these are styles associated with Tailwind. So if we go to like Tailwind docs, go to the documentation, we want to go to Tailwind version 4, get started, you can see that Tailwind has a list of all these

different keywords that they can use in your code like background color. So if I did like BG red 50, right, as a class name. So I'll put BG red 50. I'll save it. Go back to Tender. So for example, I would change this from BG white to BG red 500, which stands for background red 500. And go back to the front. I now see this header is now completely red, right? Cuz that's the Tailwind class name CSS. So, okay, cool. So I understand what all these are doing. Um, we could go to the Tailwind documents and figure out which each one of these lines are doing. Uh, but again, it's just some styling. We have an a tag, which is going to be a link. Um, we want to make sure we understand what all

these are doing. Target equals blank, which means it'll open it in a new tab. Re equals no open non-refer. This is some helpful tooling for kind of preventing tracking. Uh, class name hover text gray 600 transition colors. This is so that when we hover over it, it changes color, which is pretty cool. Um, and the like. So, you want to make sure, like I said, anytime you're using AI generated code, you understand what every single line looks like. These lines look pretty good to me. So great. Now we have a header component. We can actually reuse this all over our project. But I'm going to go one step further because I don't want to have to, you know, go to every single page and do import header blah blah blah. So instead, the last piece that we're going

to do is we're actually going to write in our layout.tsx because every single page is going to come with a header. We're going to do import header from at /components/header. And we're going to stick the header right above props. So now every single page is going to automatically come with the header, automatically come with the connect button, which is going to make navigating our site much easier, and we don't have to keep importing the header into every single page that we create.

Now, if we go back, we obviously have double headers here. On the page side, I'm just going to remove the header export default function home. And now we just have hi. Nice. Now, if your AI didn't give you a good suggestion, you can always come to the Tender UI siphon updraft course and grab the header that is located in here instead. So, feel free to always refer back to this if your vibe coding doesn't vibe that well. Okay, so step two is done. connect our wallet with a nicer connect application or a nicer connect feature. We are now connected. This is fantastic. Now, step three, implement this function. This is a pretty overloaded step. There's

obviously a lot to do here. We want to call airdrop ERC20 from this website. Remember, if we go to the actual website, we have a form to take a token address, a list of addresses, a list of amounts, a little transaction details here, a send tokens, and then we have a safe mode and an unsafe mode. For the purpose of this course, we're going to ignore the unsafe mode. That's kind of like an additional feature that the actual site has. We're just going to focus solely on this safe mode. So, we don't need this back and forth bit.

We're only going to focus on interacting with one single contract. So obviously to do this we're going to need a number of things. We should make a little component just like we made the header, right? So the header component is going to be this top part here. And then we'll make a intake form component which will be this bottom part here. And then we'll combine the two on our page. So in the layout we'll have the header here. And then on our page we'll have it render the intake form. And then if later on we want to extend the application, we can have that form show up on multiple pages. We could just implement the intake form here, you know, whatever we want to do. But I want to have it a little bit more module. So I'm going to create a new component called airdrop

form.tsx. Now, if you want to try to vibe code your way to victory here, feel free to do so. However, for a big page like this, in my experience, I found AI can get a lot of things wrong. And if you don't really know what you're doing, it can be really hard to debug what the AI gives you and get it to be correct. So, we're going to start building most of it ourself and then we'll flip back to AI to help work with the AI a little bit later cuz there's a lot of new concepts in this Airdrop form that we're going to be working with and we're finally going to work with WagMe. So, let's just go ahead. Let's get started here. Let's create this component.

export default function airdrop form. And then let's just have for now let's return little div closing div. We'll just have it say hi. And now let's stick this airdrop form in our page.tsx. So I'm going to say import airdrop form. Oh, thank you GitHub copilot right here. And then we'll just do like little autoclosing tag here. Airdrop form. Oops. This goes here. And we'll refresh Pi from Airdrop. I should say save from Airdrop. Okay, great.

Okay, so in here, we're going to want at least three input fields. A token address, recipients, and an amounts. Now, to make this kind of standard, what we can do is we're going to create a little new folder in the components called UI. And we're going to create a new component called input field.tsx. And this is just going to be some code for us to make a nice little uh input field. Now, some boring template like this, this is something that AIS are great at doing. So, we want to just be very specific and we want to tell our AI exactly what we want this to look like. So, let's go ahead and try that with DeepSeek. Again, at the time, my favorite is Claude V3.5. If you want,

there's a link to this leaderboard in the GitHub repo. associated with this course called the AI leaderboard. The chatbot arena LLM leaderboard. It's a community-driven evaluations for best LLMs and AI chatbots. For us, we specifically care about the coding chatbots. And if we scroll down in here, we can see what the leaderboard has for the top models and their rankings. This is good to use kind of as a rule of thumb, but as you can see, it has chatbt 40 latest as the number one. Gemini 2.5 is number two. I personally still think the rank 9 claw 37 model is the best.

Um, so even though these can rank differently, there's still kind of a vibesbased pick your own winner based off of what you feel is the best. So these are kind of the top ones, but for me, I still think Claude 3.7 is the best. But by the time this video comes out, that might be totally different. Anyways, so something boring like a really nicely formatted input field is something that AIS can do really well for us. So we can come to our AI of choice. Remember, feel free to use the arena leaderboard to take your pick. We might say something like I'm going to say I'm looking to make an input field React component with TSX. It should take the following props. A label, placeholder, value, type large, and onchain. The label is going to be, you know, this bit here. The placeholder is

going to be what you see, you know, when there's nothing in here. So like, you know, this would be the placeholder. This 0x would be the placeholder here or this would be the placeholder here. Value is going to be whatever is actually in it at the time. So the value of token address here would just be high at the moment. The type is going to be if it's a string, a boolean, a number, etc. large is going to be and I should tell DeepSync this I'm say whether or not the text area is large so this is like a single line this is multiple lines here and then on change and I should tell deepseek here the function to call when the area changes and I'm using tailwind for styling and let's see

what it gives me and I'm using the deep think model here again I'm using deepseek because it's free but please feel free to use whatever model you like the best all All right, and let's see what it gives us here. This looks pretty good to me. Here's a reusable input component with TypeScript that meets your requirements. I'm going to copy it. Come back over here, paste it in here, and let's see what it gave us. And let's make some tweaks in case we don't like it. So, it gave us a change event handler from React. Actually, don't like that. Uh, I am going to tweak this a bit. You might get something else. I'm going to change this to just a e react.

Change event. Oh, looks like get him copilot. It has what I want. Change event. So, if your AI didn't give you this, just go ahead and change it to this. Once again, if your AI gives you something terrible and nonsensical, copy paste what I have in here. And then what you can do is you can copy it. Bring it to your AI. Paste this in here and say, "Here is what my instructor gave me. What is the difference?" Or something like that. And be sure to read whatever AI gives you. This is a perfect opportunity to kind of work with your AI to learn what is going on for these basics so that when these get more complicated, you can actually really understand every single piece of your application. So, export default function input field. That's fine. Label

placeholder value type equals text large equals false on change. That looks good to me. And it all of this code here. So, we have a little label here that it's creating. Great. we have this large conditional. Okay, if it is large, the style uh the number of rows is different. Otherwise, there's just going to be one row. Uh and this looks pretty good to me. So, let's try this out. Let's try this input field out on our Airdrop form. So, I'm going to do import. Oh, and at the top, I want to do use client. I'm going to say import input field from at/components field. Oh, and this is the

default. So, we don't need that there. And now in here, instead of hi from airdrop, I'm going to have a couple different input fields. So, we'll do input field here. Label will equal the token address. Like so. Why do I have a squiggly here? It's missing the properties. Okay. Yep, makes sense. Place holder is going to equal 0x. The value is going to equal well we want the value to equal whatever the user puts in here. So I'm actually going to create a hook at the top which I'll talk about in a second. const token address, set token address equals use state like this. And

then we'll import use state from React. I'll talk about this in a little bit if you're not familiar with that. So we'll say value equals token address. We'll say onchange equals e arrow function set token address e.target value and backslash like this. Okay, cool. So on our UI now we now have a little input field where we have this little token address label. Okay, cool. So this is actually looks pretty good.

Thanks DeepSeek. Now, let's briefly talk about this use state thing here. If you're unfamiliar with React, this is called a state hook. In normal JavaScript, whenever we want a variable, we would just say let token address, you know, var equals this. And if we wanted to change it, we would just say, okay, token address var now equals this blah blah blah blah blah or whatever. But in React instead of doing that we use this state hook mechanism.

So it's very weird but const token address is essentially the same as doing let token address equal and then whatever's inside of the use state. So equal this and set token address is essentially a function to update this variable. So like I said instead of doing token address equals you know some new address blah blah blah blah blah uh we call set token address. So the equivalent of doing this with this state hook would just be you know set token address to this. Now this is very bizarre and kind of weird syntax if you're not familiar with it. But the reason that we do this is because we need a way to tell our website to

rerender or update whenever this variable changes. So in our input field, we're saying every single time something in here changes, we should update this token address. And this is a lot more minimal than us writing some code of like okay like if um that field thing that we made changes oops you know this is some pseudo code update the token address and then rerender the whole page right that could be that could be kind of a lot of code and a lot of very messy code so this is just a much easier way to do it in React AIS are great at working with React So if this is kind of confusing to you,

take some time, ask your AI some questions, read the React documentation. But like I said, using this methodology, using this pattern will enable our website to rerender much easier than if we do like, you know, const my or I guess let my var equals 7, you know, blah blah blah blah blah. So most of the time if you have some variable that you want to update with your website, you're going to want to use this use state hook when you're working with a React component. It takes a little bit of time to getting used to, but once you're used to it, it becomes pretty easy to continue on. Okay, cool. So we have a token address field. Let's make another field. Do input field. Instead of token

address, this should be recipients or what is on the T sender? Yep, recipients. Yep. Okay, great. placeholder will be like ox blah blah blah blah blah comma o ox blah blah blah blah instead of token addresses this is going to be recipients this is going to be a new variable so we'll say const recipients set recipients set recipients okay cool it'll change the recipients okay cool to our front end now we have a recipients okay great and this is going to be large equals true like this. So now it has several rows. Okay, great.

What's next? Let's copy paste this. We want an amounts. So we'll do like a placeholder of 100, 200, 300, dot dot dot. We want a new hook here. So this is going to be amounts and set amounts equals use state. Value is going to be amounts. Function is going to be set amounts. Set amounts. Okay, that looks good. We do want this to be large. Okay, great. And then finally, and then that's it. 1 2 3 input fields. Okay, great. And the transaction details form will come in the bottom later. Okay, so if we go to our site, that looks pretty good.

Token address, recipients, and amounts. Okay, awesome. Now, once these are all filled in, we want a little button at the bottom called send tokens. So, let's create a button button. I'm going say on click equals handle submit and this will be a function that we create anytime somebody clicks this button. So I'm going to take this I'm going to scroll up. I'm going to say async function handle submit and I'm just going to do console.log hi from submit mark. Okay. Now we should have a button at the bottom. Oh submit or send tokens. Let's inspect console send tokens from submit.

All right. When I click it, we get from submit. Okay, great. The button styling is pretty terrible, but that's fine. Okay, so now we have our forms. The styling is terrible, but we'll fix that later. We're just worried about the functionality for now. We have our forms. We have our button that doesn't do anything. Let's make this actually do some stuff. Okay, now here's where I can actually show you this use state stuff kind of working, right? So in our handle submit what I can do is I can have this console.logout the token address the recipients and the amounts. So every time we change one of these inputs it calls this set recipients function which

essentially updates these variables. So now that I have these console logs in here for handle submit. If I go back to the front end do a little refresh. If I put in like 0x 0 here for recipients, I'll put in like hello here and amounts, I'll put in like 100. If I hit send tokens, it's going to print out the values that are in here because every single time we change these input fields, it automatically updates these variables, right? Which is really good, right? uh without these state hooks we would have to like create a function you know like function update token um okay and then we do like you know document

getelement by ID blah blah blah set the token address to that and you know we don't want to have to do that these state hooks do that and they do some other nice functionality for us okay cool so now we're getting the address the recipients and the amount Now we can finally connect to the blockchain and actually send this transaction. And this is where we can finally use WagMe and we can finally use our blockchain, use our Anvil blockchain to do a little bit of testing. So let's talk about how this handle submit thing is going to work. When somebody calls send tokens, the first thing that we're going to do is we're going to have to

approve the contract to have the tokens. So let's write a little playbook out here. So step one, approve our tender contract to send our tokens. Number two, if approved, and then actually 1 a, if already approved, move to step two. Step two, call the airdrop function on the tender contract and then wait for transaction to be mined. And that's pretty much it. So, let's go ahead and get started with this first one. Approve our T-enter contract to send tokens. And actually, we should flip the order of these. We should check if already approved, move to step two. Otherwise,

approve the tender contract because to move tokens through a contract, you always have to approve the contract first. So, that's what we want to do here. So, uh in order to approve the contract or check that we've approved it, we need the contract address and we need the token address. So let's first get the contract address. So we'll say const t sender address. And we can make this a constant in this variable here because we don't really need it to be a state hook, right? We only need this address when somebody's calling the submit function. And once again, we're going to follow a similar pattern to what we did in the HTML section. We want to get this from some constants file. So we're going to go ahead create a new

file, constants.ts. ts and in here we're going to get the uh the different addresses of the different uh contracts. Now to make your life easier, we can just go to the tender UI and just copy and paste the constants that we go to here. Uh we can see the tender and the tender no check addresses. For this tutorial, we're only going to be focusing on the tender. We're not going to worry about the no check. But we do see for chain 31337, we have a T sender address here.

And that's what we're going to mainly be mainly working with. We also see this comes with an 20 AI. And if we scroll down enough, we also have a Tender AI. So we can just copy this whole file, paste it into our constants.ts, save it, and that looks pretty good. Now we have this mapping here or this dictionary or this JSON object chains to tender which has a chain ID and then a T sender address and then a T sender no check address. Like I said, we're only going to be working with the Tender for this video. But now that I have this mapping back here, I can do import chains to T sender. Uh, and let's actually get the Tender AI and

the ERC20 AI as well. from at slash constants like this. And now the tender address is going to be the chain ID and the um since we're only doing the tender or excuse me uh tender address equals chain tender chain ID and tender. We do not have the chain ID though. So how do we get the chain ID? And this is important, right? Because if we're on uh a different chain, each one of these chains is going to have their own T sender address, right? So, we're going to want to get the chain ID to figure out what Tender address we're on.

So, how do we get the chain ID? Well, this is where we can finally work with Wagme. So, let's go over to the WagMe documentation to learn a little bit more about Wagme. So, if we go to WagMe, we go to React. This tells you all about how to work with WagMe and the Wagi hooks and why you might want to work with WagMe. Here, the reason we want to work with WagMe is because we have all these hooks that work really well with WagMe. One such hook is going to be use chain ID, which allows us to very quickly and easily get the chain ID as a React hook. The way we got our contract address before in our HTML project was we just had this constants file where we just had export con contract address

equals and then we just had a single address here. But because like I said we're going to be on different chains. We're going to want to get this chain ID. You can get the chain ID using VM, but it's going to be much easier for us to just use this WagMi hook. Uh import use chain ID for Wagmei. And then we can just do const chain ID equals use chain ID. And that's it. So, I'm actually going to copy this line from the documentation, pop it up at the top here. And now I can say const chain ID equals use chain ID like this. And why this is so helpful is because anytime the chain ID changes, you know, anytime the user on the front end, now I have too many things open. anytime the user

on the front end, you know, uh, updates to a different chain, this chain ID variable will automatically change for us, right? And this is again why we want to use these hooks because if we weren't using this use chain ID hook, we'd have to code our own, you know, bit of logic. We'd have to say, okay, if the user changed, if the user went to their MetaMask and they changed the chain ID, here's the new chain ID. Like, we don't want to have to code any of that. WagME has already coded all of that for us. And we get this chain ID here. And what we can do now is we can even do like to test this out little console.log uh tender address and um console.log chain ID like this. And on the front end, let's just go ahead and

refresh here. If we're looking at the console, if I type click send tokens, I get the Tender address right here. And I get the chain ID of 324 cuz I'm connected to ZKYNC. This is the zkync tender address and this is the zkync chain ID. Perfect. Okay, cool. So, let's continue handling the submit here. Now that we have the tender address, what's the first thing we need to do is we need to figure out if the contract address has already been approved. So, I want to get the approved amount. And to do so, I'm actually going to create a new function async function. get approved amount. It's going to take the T sender address which is going to be a string

type and it's going to return a promise of a number. And since it's going to return a promise, we know that we're going to have to call a wait here. And in here, we're first going to say if not T sender address, meaning if T sender address is null for some reason, like if the user is on some weird chain over here, we'll just do alert this chain only. No address found. Please use a supported chain. And then we'll turn zero since we're returning a promise that which returns resolves to a number. And now we want to read from the chain. We want to read from the chain to see if we have

approved enough tokens. On ERC20s, they have a function called allowance which tells us how many tokens somebody has approved for a certain contract. So we want to call this function. We want to call this read function. How did we do it before? How did we read from the blockchain before? Well, in VM, we created a public client. We called get balance and then we just kind of consled it to the log. Well, this is to read a balance of a contract. We want to actually call a function. So how do you actually call a static function with WagMe? Well, this is where we can go to the WagMe documentation. We go to React. We can scroll down and they

have a hook in here called use read contract. So it's a hook for calling a readonly function on a contract returning the response. So the allowance function is going to be a readonly function. So that's what we're going to call and we're going to literally follow the docs verbatim. We've actually already imported WagMe. So, we can just do import use read contract. Uh where is it? Right here, comma, use read contract. And we'll scroll down. Says use read contract. And you pass in the AI, the contract address, and the function name. And if you scroll down some more, there's a whole bunch of other parameters as well that we can do.

And that's it. Now, for this, we could use WagMe's use read contract, which is a hook here that we could use in our app. However, since we're not really since we're only calling this one time and we don't need the website to rerender when we call this function, we're actually just going to use the raw read contract function instead of the use read contract hook. Now, this is something that as you get better and better with working with front end, you'll start to figure out when you should use a hook versus when you should just call the function raw. And you can also work with your AIS to ask it questions if you're unsure if you should use a hook or just call the function raw. So we have use read contract here but we can also go back to the WagMe

documentation and we can just look up the core documentation. Scroll down and there is a read contract function right here action for calling a readonly function on a contract and returning the response. That's all we want to do. We don't need to keep track of state or storage or do any rerendering or anything like that. So we're just going to use this readonly contract here. Now, since we're calling from the core codebase, we actually have to install I'm going to cancel this for a second. The at@wag/core library. So, we can do pnmp add at wagy/core like so. And now that we've installed the core library, we can import it here. Import read contract

from atwag/core. And then in our get approved amount we can just call it directly. So we'll say const uh response equals and oops await and read contract is a promise. So we need the await keyword. So await read contract and we go to the documentation we can see how this works. So we need to pass it a config and all the parameters in here. So what is this config thing? Well, this is why way back at the beginning we created this rainbow kit because this is our config. So this export default get default config. This

is going to be our config that we're going to be working with anvil. We exported this default config and then in our providers app providers here we have wagme provider config equals config. So this config here is basically telling our application how it can interact with the blockchain and we could import this config and use it here or WagMe comes with a hook that we can use. So up at the top we can import that in. So we'll just say we're going to get use chain and we're going to do use config and we'll use this as a hook. That way same thing if the config changes for some reason it'll automatically update. But we'll say const config equals use

config. And now we can pass this to our read contract. And then we can pass in all the stuff that we actually want to pass in. So the AI is going to be the ERC20 AI. The address is going to be the token address that's going to be received from our state hook. Now, we want to make sure this is a a string that starts with ox. So we're going to say as ox dollar sign string like this. And the reason for that is because our read contract function takes in a config because that's the type that our read contract is going to need. It's going to need this ox string. So token address is

that the function name is going to be allowance. And then the args are going to be well we're going to need to check who is getting the allowance. It's going to be obviously whoever calls this function. So we're going to need to get the account here. We can get the connected account with another WagMe hook. So we're going to also use this use account hook. And so we'll say const account equals use account. So the args are going to be the account address and the t sender address as once again an ox string works like this. Okay, so doing this like this is going to be the

equivalence of like if you're doing a solidity contract, it would be like token.allowance account tender. That's essentially what we're calling but we're calling it on the front end and we're calling it with react in this read contract function here. So this response will be like how much is being approved here. So then we can say return response as a number. So this is like we'll cast a response as type number. And now that we have this function, we can call it in our handle submit. We can say const approved amount equals await get approved amount with the t sender address like this. And now this will get

how much is approved. For now let's just do console.log approved amount. So let's just kind of put this together to see if this is working at a high level. Let's finally spin up our anvil chain. Get this started from the front end just to make sure that at least this contract interaction is working, right? Because we have our first kind of reading from the blockchain. We haven't really worked with WagMe before. Let's just make sure that this kind of bit here is working as intended. So on our front end, we have our send tokens button, which doesn't do anything. Let's make this button look a little bit more like a button just so that I know that I'm even clicking it.

And to do so, I'm actually going to just prompt my AI going say, "Here's my button using Tailwind. Can you make it look a little nicer? Let's see what DeepS gives us." Okay. Wow. Deepseek kind of popped off. Um, but let's just copy this class name here cuz this is the actual styling that we want. So, let's kind of paste this in. Wow, that is quite the pop off. Um, let's see what it looks like on our UI. Do a little refresh. Oh, I should probably start the UI. Okay, cool. Now, let's do a little refresh. Okay, cool. Yeah, this does look a little bit better. Now, our button looks kind of like an actual button. So, cool. So, right now calling send tokens. I'm going to get a bunch of

errors because well, uh uh nothing is filled in. So, let's actually go ahead and let's start up our locally running node. Same as what we did in our last section. If you go to the GitHub repo associated with this section, there is already a blockchain here, tender deployed.json that we can use for our tests. So, tender deployed.json. I'm going to copy the whole raw file. New file tender deployed.json. Paste this in here. And this Anvil blockchain comes already defaulted with T-Senter deployed, some mock tokens, and then obviously some

default Anvil accounts with ETH gas already in them. So I'm going to go to my package.json. Create a new script. We'll just call it Anvil. And this will run anvil- load stateer deployed like so. And now what I can do is I can do pmppm anvil. Oh, uh, tender deploy, excuse me, or deployed. Let's recall this tender deployed. And try this again. And cool. Now I have an anvil instance running with all of my Tender stuff already deployed. So now my front end, I can switch from my account.

Let's go to let's go to the Anvil account that we imported that Anvil one and let's switch to the Anvil chain ourselves. And now we can see we have a whole bunch of ETH in here. Let me just refresh the page. Get rid of a lot of those errors. And if we go to the GitHub repo associated with this tender UICU, if we go to the contributing MD, I believe the addresses are in here. Ah, okay, cool. T sender address which is going to be in our constants.js already but we also have a mock token address.

Okay. So let's grab this and it says can use mint or mint to function to get some tokens. Let's go to our UI maybe pop open our MetaMask here. Uh let's go ahead and actually connect account so that it's account 8 connected to our UI. Let's go to tokens. We'll add import a token here. Paste a token address. Token symbol MT. Token decimals 18. That looks correct. And this account 8 gets started with 100 Mach tokens. Hopefully you can see that it's a little bit off the screen. We can also expand the view.

Yeah, 100 mock tokens. And again, if you are haven't imported those accounts, what you can do is you can grab the private key zero from your locally running Anvil instance and import it into your MetaMask and you will have this account in here. Right? So on the anvil chain, this is the imported account zero with 100 mock tokens. So on our UI, what we can do now is well, we can go back to the readme here. We can grab this mock token address, paste it in here for some recipient. Let's say send it to oursel and send, you know, 100 to ourself. Now if we call send tokens, it should console.log log the

amount approved which right now should be zero. So let's call this and we do indeed see zero and we don't get any errors which means we're doing something right here. Okay, cool. This is very exciting. Let's keep going. So we have our locally running anvil node running with t sender with our mock tokens with everything. We have our front end running. Let's keep building kind of this main function here in our airdrop form. So we have the approved amount that we just console.logged. What do we need to do next? Okay. Well, we say if we've already approved, move to step two. Uh otherwise, we need to approve first, right? So, let's go ahead and do that.

So, we'll say if result is less than the total amount needed. H how do we get the total amount needed? How do we know how much we need? Well, if we're going to send 100, then we need 100 approved. If we're going to send 100 + 100 equals 200, then we need 200 approved. Okay, so we need to count all the amounts to get the total amount needed to approve. So we need to actually create that function first. So up at the top, we're going to need to create a a new hook. We're going to need to create a new variable that changes every time amount is updated.

Well, there's a couple of different ways we could do this, but one of the ways is using something called use memo. Now, use memo is a React hook that caches the result of a function call, preventing unnecessary recalculations when the component rerenders, unless that dependency is passed it to change. So on our site here, anytime this is updated, we should recalculate the total. But we don't want this to kind of recalculate if something up here changes instead. So what we can do is we'll create this use memo thing. So we'll say const total which will be a number equals use memo and we're going to need to import this from react use memo and this is going to be a function where we call calculate

total where we take the amounts right all these amounts and we're going to add them all up. So we're going to create a function called amounts and we should call this function anytime the stuff in here changes. So anytime amounts changes. So let me just do so when we're doing use memo or use effect which we'll use later is we want to tell React to call this function whenever something changes. This is the list of dependencies.

So if we kind of back this up, this is a valid line of react. What this says is anytime the amounts variable changes, call a function of console.log high. And we can even just delete the cons here. And I'll show you on the UI. Let me do a little refresh here. So if I change stuff in here, you know, nothing's printed. If I change stuff in here, nothing's printed. But if I change something in here, it prints out hi. So that's how this works. This is a list of dependencies in this array. So it's saying anytime amounts changes do this function. So let me back it up here. So what we want to do is we want to say

anytime the amounts changes recalculate the total and save that as a variable called total. So we need to create a function called calculate total. So we could put that right in the component here. However, I want to set it up as a util just in case we want to use it later and we want to use it in other components. And this allows us to test it much easier if we separate it here which we will be testing it. We will be writing some unit tests for this. So let's create a little util here utils. We'll create a new folder called calculate total. And in here let's create calculate to and in here new fileate total.t ts. Now, this is going to be ts, not

tsx, because this is just going to be raw TypeScript. All we're going to have in here is the function calculate total. And we're going to take the amounts as an input, and then we're going to get back the calculated total. And once again, here is where AI can be really helpful. I can copy this line, go over to my AI, and I can say, here is a use memo I'm going to use. So, I'm going to ask it. Here's a use memo I'm going to use. I have the three back ticks. I paste it right in here. I want to make a calculate total function which takes a string in the format and I paste a potential format here and adds all the numbers up. It can be comma or new line delimited. Can you make such a function

in TS? So, this will allow the user to create new lines or separate them with commas. And I could 100% code this myself. And if you want to get some practice coding such a function yourself, you should. But I'm going to go ahead enter and let's see what DeepSync give and let's see what deepse gives us. Okay, here's what DeepSync gave me. I'm going to copy this. I'm going to paste it into my calculate total.ts and I'm going to see if this is what I want. Okay, so we have it. And again, whenever you copy paste code from an AI, you want to read it line by line to make sure it's doing what you want it to do. I'm going to go ahead and spot check this and if you don't understand

any of the lines here, now's a great time to pause and check what your AI gave you and then maybe compare to what we have in the GitHub repo associated with this course. Okay, so amounts is going to be a string. It's going to return a number. This looks good to me. Con numbers equals amounts.split. So this is some syntax to split with a new line or a comma. That almost looks good. I think they need a plus here. Yeah, they need a plus here. So, this this is where you can get tripped up. This might look good to kind of the naked eye, but because I know what it's supposed to be, I know it's actually missing a plus. Um, the plus means that any new lines or any commas more than one. Without the plus, it's

just kind of one. Uh, which isn't great. So, I'm going to add the plus here. Okay. So, then it say mapap string to string.trim. I don't like the string keyword. I wish it would name it amt for like amount. So I'm going to rename that. Uh the trim is good. This removes whites space. We like that. Filter amount should not be equal to empty. This is also good. We like that. And then it converts to numbers. This is probably okay, but I'd actually rather do amount to parse float amt. Um oops, excuse me.

Amt.parseflat. Uh, I'd rather do it like this. That's fine then. Okay. Um, I don't like the name numbers. I prefer amount array. Okay. And now I'm going to do if amount array do sum is nan or is not a number is nan. Then we're just going to say return return zero. And now at the bottom return number or amount array. Uh oh, it actually did that here. Actually, that that actually might be better. You know what? Actually, I do like what it does here. So, I'm actually going to get rid of this. Okay, so we're going to filter out all the not numbers.

Okay, that's good. And then reduce some number sum plus number zero. Okay, cool. So then we're going to just basically this is the kind of key line that actually does the addition. This is kind of the all the text parsing. And now that I've kind of reviewed it, it looks pretty good to me. But I want to make sure this is actually really good. So we're going to write our first unit test on a full stack on our codebase here, which is really exciting. So like I said, in the blockchain world, getting things right is really important. It's really bad if you get something wrong. And as we're

building and you know iterating on our websites, we're often going to make changes. So we want to have a test suite. We want to have tests for our codebase here. And the reason I set up this kind of weird format of putting this in a folder and then having this calculate total folder and then calculate total.ts file is so that next to our our calculate total.ts, we can have a new file called calculate total.est.ts. ts and we can have our test right inside of here. Now, if you're familiar with like Python, you might be more familiar with tests being in their own folder. If you're familiar with Solidity, tests are often in their own folder. In full stack applications, tests can often live

alongside the files themselves. In Rust code, you'll actually find pretty popular that often tests are inside the files themselves. So, there's a lot of different places that tests are often put and it's kind of up to you to figure out the methodology that you like best because you'll see it a lot of different ways in a lot of different places. For us, for this course, we're going to be putting our unit tests um in files alongside the actual files and then we'll be putting endtoend tests in their own folder. And we'll talk about endtoend tests a little bit later. Now, if you're just trying to win a hackathon and code very quickly, you might skip doing some of the tests. However, in the long run, it's often much much better to have tests early on so that when you iterate and when you make changes, you

can know that you're not going to break anything. So, we're going to actually write some tests here. Now, again, this is where AI is very helpful where you can ask it to help you write tests. However, once again, if we're going to be using AI to help us write tests, we absolutely want to make sure that the code it gives us is right. Because what is bad is if we use AI to write our functions and then use AI to write our tests and we don't check what the AI gives us, it's just going to give us garbage and then we're going to test garbage with garbage and we're just going to get more garbage. So if you use AI to help write your tests, you absolutely 1,000 million% need to check what the AI is giving you. So I'm going to ask the AI to write us a test and

then I'm going to spot check it. If you ask your AI to write you a test, be sure to spot check it or be sure to compare it with how we did it in the GitHub repo associated with this course. I'm going to say great here's what I said to my AI. I said great here's what I'm going with the three back ticks the full function and then at the bottom I say can you write me a test for this now I'm going to use vit test. You can use whatever testing methodology you want. However, for our unit tests here, I'm going to use V test and I'm going to say and have the test be in the same dur and I'll hit enter. We'll see what it gives us. And once again, we're going to edit it based off of its output. So, it gave

us a little output. I'm going to copy paste it and let's see how it did. Paste it in here. Zoom out a little bit. Describe expect it from V test which we need to install. So, let's go ahead and look this up. Revi test v test get started. pmppm add- v test. The dashd means it is a dev dependency. This isn't something we need to actually build the website. It's only for testing. So I'm actually going to kill this for now. Although I probably don't need to, but let's run this anyways. And we'll add v test. I don't know how to pronounce it, but that's okay. Um, that red squiggly line should automatically go away. If it does not, we can restart the TS server.

And instead of this, we can just do dot slash calculate total cuz it's going to be in the same folder, right? These are in the same folder here. Um, and again, we can restart the TS server to get rid of that little squiggly. Uh, or maybe not is not a module. Ah, and then we also have to add Oh, maybe did uh Deepseek give us this? Uh, yep. Add it. Aha. Yep. Create a vit.config.ts. We sure need to do that. I'm going to paste or create new file. V.config. config.ts. Paste it in here. Actually, instead of vit I'm going to do vit test.config. MTS. So, this is where DeepS didn't quite give me what I wanted. Um, and we're going to import

define config from the test /config. And for plugins, we're going to do plugins ts config paths. And so we need to import this to work with Typescript. Import ts config paths from vit ts config / paths. And all this is available on configuring vest. And this is in the documentation here. It looks like deepseek might not be the most upto-date uh sad face, but we need to add this as well. So, I'm going to do PM PM add-d vit tsconfig paths to work with t.

Uh, this is a tool that allows Typescript paths to basically work. And we're working with TypeScript, so we're going to want to do that. And we don't want uh for tests. For here, we're going to say depths for dependencies say inline wagme at wagme/core. We need a comma here. And then we want to do exclude um exclude stuff actually. Uh you know what actually to make this a little bit easier I'm just going to tell you to copy paste mine from the GitHub repo. So we're going to go to the GitHub repo associated with this. We're going to go to the bestm and I'm just going to have you copy paste this and then I'll explain what all this stuff does. So TS config paths like I said this allows vest to

work with TypeScript environment. This environment JSDOM uses JSDO DOM to simulate a browser environment for your tests. Um, this is important because since we're going to be testing, since our app is going to be running in a browser environment, that's the environment that we want to test. This exclude is really important. We don't want to accidentally run any tests from node modules or from a test folder or from playright or from TS results. We want to only run those V test um sections. And this dependencies thing tells Vest to inline the WagMe and Wagmei core libraries instead of mocking them. This means that the libraries would be like kind of bundled or copy paste directly into our tests instead of

them being externalized. This actually isn't really super important since we're not doing any WagMe or Wagme core tests. Um but if you decide to add those, this is something that you would need to add. Anyways, now that we've added all that, we should be able to restart the TS server. That squiggly line should finally go away. Looks like it's not. We're getting the calculate total.ts is not a module. And that's Oh, I did all that work kind of for nothing. Um, that's because we need to export this export function. Calculate total. And now this goes away. Okay, cool. So, now that we've done all that, we can finally look at what it's given us for some tests. So, calculate total. It should handle basic cases. I actually kind of

hate this as a test. Um, I would rather this be like it uh should work with new lines, you know, 100 plus 200 to be 300. I'm actually going to copy paste this. 100 plus 200 300 should be 600. It should handle mixed delimiters. I like that. 100 200. New line 300. That should be 600. Good. 1.5 2.5 3.5 7.5. Good. some messed up stuff here to be 900. Okay, that looks good. It should handle empty input. I like that. It should ignore invalid numbers. I like that. It should handle decimals. I like that. It should handle whites space. That's a

good test. It should handle complex combinations. That's fine, too. Okay, cool. Now, to run this, we can just do pmppm run test. Excuse me. Let's go to our package.json. Let's create a new script here. We'll call it test unit. And this will just run vest like this. And then we can do pmppm run test unit. And it'll run vest. Oh, and uh can I find dependency jsdom? Would you like to install it? Yes. Forgot to install that. Okay. And let's rerun the command to actually run the test. And it failed.

Let's figure out why this failed. Calculate total should ignore invalid numbers. expected 57 to be 45. So should ignore invalid numbers. Failed for us. Let's go to that file. What did it do? Expected 57 to be 45. So this is the line that failed. 12. Oh, so it it did 12. New line 45. Oh, that's kind of cool. So it actually correctly added 12 to 45. I actually kind of like that functionality. So if someone does like 123 or 12. So if someone did like on our our website Oh, I close it. But if someone did 123 and 45, it would actually do 57. I actually like that. So

I think that that's good functionality. So I'm going to keep that in there. And once I save, it's actually going to automatically rerun. I'll print Q to quit. But I can run that again. And it'll print out all the different tests. One files passed. Seven of the actual tests pass. And that looks good to me. What's nice about autogenerating tests is even if you disagree with them, like I kind of did, you can very easily edit them based off what the AI gives you to give you kind of what you want. So mine gave me this expectation that 12 + 45 here uh would just kind of exclude this one. But because of the way that I created the calculate test, it actually added added them up to 57. I actually like that functionality. So I'm going to

keep it as such. And cool. So now we have a test for our calculate function, our calculate total function. And this is really important because we, like I said, we want to make sure our UIs add stuff up correctly because if it adds something up wrong and we send a transaction with the wrong values, that's obviously uh very bad. So cool. So now we have a calculate total function. We can finally import it. We've tested it. import calculate total from at utils/calculate total and it's mad because at that's not where it's located. One other thing that a lot of people do is for their utils they'll generally create a

index.ts file. Um and in here what they'll do is you'll just do export calculate total. Thank you from uh /calculate total.calcalculate calculate total. And this way in our airdrop form instead of doing kind of this giant piece of code here, we can just say import calculate total from slashutils. Right? So it's index.ts will be a file with all of our utils kind of added into it. And then no matter what util we want to use, we just say import calculate total from at utils like that.

Cool. So now we have a calculate total. We can finally figure out how much we need to approve. So, we're going to have this total amount that's going to keep changing every time amounts changes and it's going to keep being updated. Um, and we can actually even see that. Let me let me restart it up. PMP rundev and we'll refresh the UI. Hopefully, I didn't break anything too too hard. But for totals, oh, connection refused. Yep. It's cuz let's also do our PMP anvil. Let's also run our anvil node. Now let's do a little refresh here. So um now what we can do is we could even have our

calculate totals. We could even print out kind of the the final bit here. So we could do instead of calling calculate totals, we could say console.log calculate totals like this. And then instead of saving it as total, we'll just do that. Now in our front end, if I say 100, looks like it's adding correctly. If I create a new line, do another 100. Now it's showing 200 correctly. If I add 200, it's turning out 400. And you can see every time I update this, it recalculates and adds them all together, which is what we want. Perfect. So, let's undo that. Great. Okay. So, now we have our our total here. Now, we can finally say

if the approved amount is less than the total, which is going to be the total needed, then we're going to want to call approve. And this is where once again, we go back to WagMe. We could either use a React hook to do this or just call the contract directly. Now, since the result of us calling this approve function, we are going to want to make some UI changes based off the answer here. We're actually going to do something a little bit funky. So, if we go to the React docs of the WagMe, there is a use write contract hook which we're going to be using. So, let's go ahead. Let's import

this from WagMe. Let's do use write contract. And in the documentation, it gives us an example of how to use it, the different parameters. I actually want to scroll down to the return type and see what it returns. It returns a number of things. It returns write contract, which is a function we can directly call. It returns data, which is going to be when it correctly works, it's going to be the hash returned. It has a write contract async which gives us a function to asynchronously call the write contract function. And that's this is what we're going to use as opposed to the just regular write contract function. It also returns an error. If there's an error, there is an is

error/isidle/ispending to tell us when the transaction is going through. And these are all different things that maybe we want to capture to update the UI based off of this. So, for example, when we send a transaction and we're waiting for it to be confirmed on the blockchain, we can have the UI basically say, hey, if the transaction's pending, maybe show like a little spinning animation. Hey, once you get the hash, maybe do this. These are all variables we can capture directly from this use write contract hook. And this is very nice because we don't have to code all that functionality oursel. So, we can get some of those in particular. So we can get that data piece which we'll just call the hash because that's essentially what it's going to be returning. This way we can do something on the UI once

we send the transaction and we'll get the write contract async function equals use write contract like this so that we can call different functions directly with this write contract async function. Now that we have that function we can actually call the approve function down here. So we'll say const approval hash equals await write contract async. And this is where we put pretty similar to the read function up here. We'll put all the parameters of what we need to call approve. Now we don't need to add the config like what we needed to do for read contract since this write contract

async is coming directly from the hook. Right? If we scroll back up, read contract is just coming from the core. Use write contract is coming from Wagy and this is a hook. And since it's a hook, it already kind of has context of our config and our state and all this other stuff. So for write contract async ERC20 AI uh excuse me, the address is going to be the token address once again as an ox string. The function name is going to be approved. Thank you Chad should be T and the args are going to be the T sender arg as string and then the total. Now to be specific I want this total to be a

big int. Um so I'm going to wrap it in this big int. I'm going to wrap it in this big int wrapper here because our calculate total returns it as just a number. But for a lot of our function calls it's much easier just to wrap it as a big int so you don't get any weird oddities. So this is how I'm going to do it. So this will give us the hash of the transaction once we sent it to the blockchain. But we want to wait for this transaction to get mined. Right? So a little confusing. We first send the transaction in our MetaMask. Then we want to wait for it to get mined. To do that, once again, we can decide if we want to just wait directly by using a function or we want to use a hook. For me, I don't want to use a hook. I just

kind of want to wait directly. Once again, the more you work with this kind of the better you'll get at figuring out which one you want to use. So since I want to just call this directly, I'm just going to grab it from the wag/core. Wait for transaction receipt. So now I'm going to say constall receipt equals await wait for transaction receipt. And since I'm calling it directly, I do once again need to pass the config since it's not coming from the hook. And I'm just going to say the hash is the approval hash. So if we go to the docs for this, oh, did I spell that right? Response. If we go to the docs for the weight transaction receipt on Wagmei, we can see some basic usage here. You pass the config, you

pass the hash, and that will wait for that transaction to finish mining. Cool. So once we've done that, if it's approved, we can say console.log approval confirmed and then we'll do comma approval receipt right here. So, since this is our first time actually writing a contract, sending a transaction, I actually do want to test this out kind of manually to make sure it's actually working. So, let's make sure our website's up. Let's make sure our note is up. That looks good. Let's go to our website and let's try a sample transaction here. So, if we go back to here, let's go find the contributing file. Let's go back down. Let's go find

the mock token address. Okay, great. It's going to be this address here. Paste it in. Let's say recipients are going to be our self. Amount, let's say, is just 100. Let's call send tokens. And okay, we get an approval transaction popping up. And my MetaMask actually says this is a deceptive request. If you approve this request, you might lose your assets. Malicious address found in transaction. Something doesn't look right. So MetaMask comes with this tool called Blockade which helps block malicious transactions since we're actually kind of testing and coding like we don't care especially because we're on the Anvil network. So Blockade this is great but you're kind of killing us here. Um we scroll down we can see

account balance 100 MT spending cap 01 MT network fee blah blah blah. Uh we scroll all the way to the bottom. We can see data. We're calling the approve function on this contract for this amount. And this is actually correct. This is what we want. Uh the confirm is red because like I said again um as of recording the blockade tool is telling us this is a deceptive request, but again we're on a local blockchain and we're just kind of testing. So this is fine. So I'm going to go ahead and hit confirm. Quest is suspicious. That's fine. And in my console.log I have approval confirmed. And I have all these notes for the confirmed approval.

Awesome. Okay, cool. So now I've got it confirmed. Well, once it's actually confirmed, then we want to actually call the function of airdropc20, right? So I'm going to say else let's call await write contract async. AI is going to be the T sender. AI address is going to be the T sender address as ox dollar sign string. Oops. We're finally going to call drop ERC20 and now the args are going to be first the token address. Then we're going to need these

recipients. So I'm going to kind of and these once again can be new lines, they can be commas, they can be all this stuff. Uh, we could ask our AI. Um, I'm going to tell you, you know, it might be a little easier to cheat. Um, because your AI might screw this up. So, if you go to components, airdrop form.tsx, uh, if you want to scroll down here and just copy these args. Uh, don't worry, I'll explain this in a second. Uh, so this is kind of a cheaty way to get these. This is going to split the recipients up into new lines or commas.

And it's going to remove all the white space. And it's going to remove all addresses that are blank. And then it's going to do the same thing with the amounts. It's going to split them all up. It's going to take out the new lines. We could have like a function that does this. And we probably would want to to make sure that this code actually works, but I'm just going to say go ahead and use this for now. And those args look pretty good to me. Something else we want to do though, and this is a little bit redundant and we probably could refactor this to make this a little bit better, is above in this conditional after somebody approves, they should also call this.

Um, so now kind of the conditional says, okay, if approved amount is already good, just directly call the airdrop and if it's not good, first approve and then directly call the airdrop. Right? So we have both edge cases covered. There's probably a better way to refactor this code to do that, but uh I digress. So, now that we've done that, let's go ahead. Let's test this out on the UI. And this should essentially be the main functionality that we're looking for out of this. So, I'm going to go back to the contributing page to get that mock token address again. Let's copy this. Back to the T-senter site, paste it in.

Recipients are just going to say, uh, you know what? Let's choose Anvil 5 cuz Anvil 5 should not have any tokens. So, let's do just pick another address that doesn't have any tokens. Let's expand the view. So, I have a different Anvil address added in here. I'm going to go ahead tokens. Import tokens. Paste this in. Next, import. Great. This person has zero. So, I'm going to copy this address. Paste this in here. Let's send them 100. 1 2 3 4 5 6 7 8 9 10. Or yeah, 100. 1 2 3 4 5 or let's send them 10. 2 3 4 5 6 7 8 cuz the MT token has 18 decimals. So we'll send them this many.

And let's go ahead and try this. So since I already approved, it should automatically just call the airdrop ERC20 function. Let's call this. And okay, this looks pretty good. Spending cap request. Oh, no. It's calling the approval again. Uh, it's calling it's calling this cake NFT because um I named it this, but this is actually the empty token. So, let's recall it that empty token. Okay. Spending cap is going to be 10 on the empty token. The spender is going to be this address here. This does indeed look like the tender anvil address. Okay, cool. And basically we're calling approve on the anvil on the t sender for this amount. So let's go

ahead and confirm. This is the anvil chain so I don't care. Yep. Confirm. Send it. And we see airdrop approval confirmed in our console.log. And then we immediately get prompted with another transaction which is good. This is what we want. And if we scroll down, it's actually decoding our function to call airdrop ERC20 with the empty token. Parameter number two is anvil number five. Parameter number three is this much. And this is the total amount. So basically, we're sending this much token to this address. This is good. Let's confirm. I have acknowledged. Let's confirm. And nothing happens because we didn't have anything print out. But if we go to our we go to back to our

MetaMask here. Let's go back to my account 8 which is the anvil account zero. Uh let me refresh list. Uh let's go back to tokens. It now says I have 90 MT. And if I switch over to anvil 5 now says I have 10 MT. So this worked perfectly. This is so exciting. Our UI actually works. it was able to call AirDrop ERC20 with these fields and send a different address this much. Now, we could add multiple recipients. We could add multiple amounts and it should all work the same. If you want to go ahead and test that out, feel free to do so.

But just to make sure, let's try this with some more addresses. So, let's pick just another address. Maybe one that's in your account already. Paste it in. We'll do another 10 here. Let's just make sure that our main address, our account 8 has enough. Okay, it has 90. So 10 + 10 is 20. So that should be plenty. Remember that these tokens have 18 zeros. So this is the amount in here. That's great. So let's call it with two recipients and two amounts. Let's call send tokens. Okay, great. The first thing we're getting is an approval request. This is actually what we want.

This is good. And again, block 8 is telling us this is potentially malicious, but we're on a local network, so we don't care. So, it's saying we should be spending 20 on the tender address. This is correct because we have 10 + 10 is 20. Okay, awesome. That's good. We're calling approve on that address for this amount. Great. Let's go ahead and confirm this. Cool. And it looks like that went through. We have a little console.log here. And okay, great. It immediately prompts us to call the airdrop ERC20. Now that that's done, item one, item two. And this time we have two addresses that we're sending.

We're sending to annual 5 and account number one each for 10 tokens. You know, obviously with 18 zeros each for a total of 20. This looks correct as well. Let's confirm. And if I pull up my MetaMask, it looks like that actually went through. We can make sure it did. Let's go to Anvil 5. Actually, let's just open MetaMask here. We'll go to Anvil 5. I'm going to refresh my list and wait a little bit for the MetaMask to catch up. This might take a few minutes. Might even just hard refresh the page. Go to tokens again. Aha, 10 was sent here. And let's go to the other address that I used. Oh, looks like I need to add this token. Import tokens. Paste it in. Next,

import. And I see 10 tokens in here too. Oh, it worked. Oh, this is super. Ah, this is so exciting. We actually have at this point a functioning working. This is fantastic and this is incredibly minimal. This is all the code that we need. So, at this point, you should be incredibly excited for yourself because you've done some awesome work. You've built a minimal app using WagMe, using these hooks to interact with the blockchain in a seamless way. And essentially, this is all the code that you needed. Obviously, this isn't the best user experience, right? When I hit

send tokens, nothing on my UI kind of prompts the user that MetaMask popped up. If I cancel, it doesn't really tell me that there's an issue. I don't really know which token I'm working with here. And that can all be kind of an issue. So ideally we and the formatting of all this is kind of gross as well. Doesn't look very nice. There's a lot of work we can do to make this better. Additionally, ready? Watch this. When I refresh the page, everything gets deleted. And that's kind of really annoying. So there's a lot of features we can add to this website to make it nicer, to make it easier for people to use, and add a lot of quality of life improvements. We're going to touch on some of those, but I am going to add

them as a challenge. Um, but additionally, but basically, we have the functionality for this done, and this is incredibly exciting. So, I'm going to go over some quality of life improvements we can give and we're going to deploy to Fleek. But before we do that, I want you to take a break and I want you to make sure that everything that we just went over, you actually understand cuz we went through kind of a lot quickly. I skipped over some details, but this is where your AI friends are going to come in help a lot. A lot of this stuff can be really confusing, but be sure that anything you don't understand, you pop into your AI buddy, you ask on the GitHub repo associated with this course, etc., so that you can make sure you absolutely know what your website is

doing. Because again, getting things absolutely correct is essential to working in the blockchain space. So, for now though, I want you to take a break. Maybe go grab some ice cream. Go grab some coffee because getting this far is so fantastic. Congratulations for getting this. Take that break. Go get some coffee. And I'll see you soon. All right, welcome back. At this point in your coding, you should have a very minimal website working. You should it should be able to take a token address, a list of recipients, and a list of amounts. When you call send tokens, MetaMask will pop up, prompt you, and you should be able to airdrop 20 tokens to multiple different people.

It doesn't look very nice, doesn't perform very nice. So, let's talk about some quality of life improvements that we can make. Now, I'm not going to have you do these. Now, I'm not going to walk you through these, but I am going to challenge you to pause the video and take some time to actually go through these and make these yourself. So, here are the different quality of life features that I want you to add to this app. And you can refer back to my app to see how I did it, but I want you to try to do this yourself. And yes, you should use AI to help you here. So, here's what I want you to add. Some quality of life features. You should both have a little spinner while the app is sending a transaction and while the MetaMask is popped up. So, like if you're on your

Tender here, well, and you had sufficient balance here and you called send tokens, when a MetaMask pops up, you get like a little spinner saying confirming in wallet. And additionally, when you confirm it, you should have a little spinner that says maybe mining transaction or sending transaction or waiting for it to go. That's like a nice piece of improvement. The next improvement was is when you add a token maybe add like a little box down here for transaction details says the token name the amount the amount in way and the amount of tokens. So for example if I had 100 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 9 10 it says okay that's this number in way and this number in the tokens cuz

it has 18 decimal places. If I added more tokens, it'll add them all up and put them in the bottom for this transaction details. It'll read the name of the token address. It'll read the name of the token based off the contract and place it down here. So, it should have a little spinner for when you're sending. It should have a little box for the details of the token. And then finally, when you refresh the inputs, the inputs that you put in should stay the same. So, this is going to be my challenge to you to work with an AI to add these features into here. Now, I'll give you a hint. I mean, obviously, the final code that you can use is on the GitHub repo associated with this, but I'll give you a hint for all of these. You're going to want to be

working with some use effects for a lot of these, saving some things to local storage. For the refreshing part, for the little spinner, you're going to want to have some conditionals on the button where you show a spinner depending on if you're confirming, if you're sending the transactions, etc. And for the little details box, you're going to want to read some details from the blockchain like the decimals, the name, and the balance of the account address. Something important to know is in the repo that I used, I use this hook called use read contracts, which is a hook to make multiple calls in a single call. So

you can read more about this in the documentation, but basically instead of calling decimals, name, and balance of in three separate API calls, you call all three of them in a single API call with use read contracts. And this will make your website more performant and it's good to know and it's good to use. But those are going to be your hints. I highly recommend you take some time to do these challenges because spending the time to work on this, work with the AI, work through the documentation, and then obviously refer back to my code when you get stuck is going to make you really, really good at understanding every single line of code that's going on here. So, I highly recommend you pause the video, take some time to go through

this, do these challenges, and come back. Now, these challenges might take a few days. They might take you a few hours. You might work with an AI and get them done in an hour. If it takes you a week, if it takes you a few days, if it takes you an hour, it doesn't matter. Just spend the time to go through these. Refer back to my code to get ideas, to get inspiration. And I really want you to go through these because it's doing these challenges that's really going to make you better. Remember, repetition is the mother of skill. And I want you to repeat doing a lot of these coding exercises to make you better so that you can understand everything that's going on. So, pause the video, try to get through some of these yourself, and if you get lost, come to the codebase to

review, work with an AI, and I'll see you soon. All right, welcome back. So, now we're going to get to our final piece of deploy to fleek. However, I lied to you. We're not going to deploy to fleek yet. We're going to take a quick stop at ETOE testing. Now, I'm going to show you end-to-end testing and we're going to show you what it looks like in the blockchain space and what it is and why it's important. However, we're not going to build a fullyfledged end-to-end testing suite because like I said, this is more of a crash course to really just introduce you to the main concepts of web 3 full stack engineering. And endto-end testing is especially

important for web 3 full stack engineering because like I said, if you get something wrong, somebody says that a transaction that is incorrect, that's not good. So, we want to make sure that our entire application does what we want it to do. So, first we're going to teach you the basics of ETE testing or end toend testing. And then finally, we're going to deploy this application to Fleek. Yes, you will have a website that you will have deployed, which is fantastic. And then we're going to be done with this section. So, let's go ahead and talk about endto-end testing. So, if you ask an AI, it's going to give you a rundown of what end toend testing is, but essentially it's a testing methodology that validates the entire application workflow from start to finish. And it gives a whole bunch of other context. An example of Cypress,

some challenges, and it actually gives it a pretty good definition. I'll give my definition as well. When we did this test here, this was known as a unit test. So unit test is testing a single feature or a single function of your test. It's a very isolated test here. The definitions can be a bit of a gradient rather than super defined, but essentially the more isolated your testing is, the more of a unit test it is. So this test here is a unit test. Now when we ran our our codebase and we showed up here, we added a contract address, you know, we added some recipients, we added some amounts, we hit send tokens, metamask popped up, we hit review, we hit confirm, we check the console.log, we check the addresses.

That was an example of a manual endto-end test. That's us testing all the different features and all the different components and all the pages and all the functions all working together to make sure we get the final output and the final outcome that we want for our application. The final outcome is obviously making sure that all the recipients get all their tokens based off the transactions that we create on our front end. Doing this manually is great. However, if we have a thousand contracts, if we have a 100 different options, if we have different buttons, if we have different spinning things, if we have different functionalities we want our website to do, well, it can be harder to manually

test them every time we make a change. And it's much better if we have an automated way of testing all of this. And that's where our ETE testing of our full stack application comes into play. And this is incredibly important for working with web 3 once again because why? Well, we want to make sure that the transactions that we create are correct. Yes, the user should validate them that they're actually correct, but we want to make make sure that our application does what we want it to do, right? There's even more there's more egregious examples of why we want this to be correct, such as security reasons, but for the purpose of this course, this crash course, we want to just make sure that the website does what we want it to do. An incredibly basic thing that we

could test for is okay when we disconnect and when we connect, you know, our website shows what we want it to show. So if I disconnect here, right, let me disconnect, disconnect, then I refresh the page. Maybe what would be better is down here it says, hey, please connect your wallet or something like that. So let's add one more feature to our website so that we can do a little endto-end testing here. Let's make sure that when a user is not connected, nothing here shows up. Right? So on the Tender website, if I go ahead and I disconnect my MetaMask here, it says, "Please connect to wall." So let's add that functionality in. So in our components, let's actually make a new component called home

content.tsx. And in here, we're just going to have a component that's literally just going to have the AirDrop form only show up if we're connected. So let's go ahead let's add this. So we'll do export default function home content and we'll do return divi from home content and instead of our page uh oh actually and then we'll do import airdrop form from at components slashairdrop form at slashcomponents like that. And then we'll just do airdrop form. Oops. Like this. And now

for our page. There's our page. Instead of using the airdrop form, we'll import home content from at components/home content. And we'll do home content here instead. And now if I do a refresh in this page, it's showing the same thing. Okay, that's cool. So now let's just add a feature here where we check if we are actually connected. Now, WagMe has a hook called use account which comes with a variable called is connected. So, we'll do import use account from WagMe. And in here, we can say const is connected equals use account. And now in here, we'll just have a little

conditional. We'll say if we're not is connected question mark. Then we'll just say you know div uh please connect a wallet. We'll semicolon here. This is known as a tertiary operator. And if we are connected then we'll say div uh back slashd. We'll add the home content in here. Then we get rid of these. Get rid of this. And we should sorry we should put all of this inside of a div after all. So let's do this. And okay, cool.

So now we have a little conditional, a little tertiary operator where if we're not connected, we'll say please connect a wallet. Otherwise, we'll show this. And we could actually make this even easier. We can say if we are connected, yeah, let's let's flip these. If we are connected, do this. And if we're not connected, do this. Maybe this is a little easier conceptually to read. So is connected question mark. This this is basically saying if connected show this otherwise show this. So now that we have this code let's go look at our front end. Let's do a little refresh attempt to call use account from the server but use account is on the client. Oh that's because I need to add use client here.

Let's save. Refresh. Please connect to wallet. Let's connect MetaMask connect. And now uh it shows the airdrop form. Let's go ahead. Let's disconnect. Disconnect. And it says, "Please connect the wallet." Okay, great. So, let's test at least this functionality with end to end testing, right? And the reason I'm showing this is because this is a kind of a complicated thing to test, right? In order for us to test this, well, we have to connect our MetaMask, right? And that's kind of a weird thing to test programmatically. It's kind of easy to do manually, right? I just come to the website and I hit connect wallet. But how do we test this programmatically?

Well, I'm going to teach you, my friends. And then you could use this power to go off and build web3 applications where you actually do test the end-to-end functionality so that you make sure your website does what you want it to do, which as of today a lot of projects don't do, which isn't great. So, let's go ahead and let's test this. Now, to build this and to test this, we're going to be using a project called Sinress combined with a project called Playright. Now, Sinress was created. Now, Sinress is an ET testing framework based off Cypress. Now, Sinress is an ET testing framework built by the Synthetics team. So, Synthetics is a D5 application. They've pioneered a lot of

developer tools and strategies in the D5 ecosystem, including, as it turns out, full stack development, and we're going to be using this tool to help us build our ET testing. Now, it's an open source project. It's a little finicky to work with, but it's kind of the best that Web3 has as of today, which if you're interested in helping out the Synress team, I highly recommend you do so because, as you'll find out, there's some gimmicky features as of recording that I would love to have smoothed out in the future. So, what are we going to be testing here? Well, when we're connected, we see the AirDrop form, and when we're disconnected, we do not.

That's what we're going to test from an ETE testing perspective. So let's go to the Simpress documentation and let's get started here. So first we need to add this in and and this is going to be a developer dependency. So we're actually and let's kill everything. Let's kill that. Let's kill this. And we'll do pmppm add-in. This is going to be a developer dependency. This isn't needed to build the actual website. And we're going to be using Playright because Playright is a more popular ET testing framework that a lot of full stack React projects use.

So Sinress has plugins for both Playright and Cypress. Cypress is another popular fullstack testing framework, but we're going to be using Playright because that's what I like better. So to get started with Playright, we're going to run this initialization command. So this is the mpm way to do this. They don't have the pmppm way to do this. But the pmppm way to do this is pmppm. And let's actually get rid of this so we can just focus on this pmppm create playright at latest. And this will install playright and set everything up for us. It'll set up a folder. It'll set up configs. It'll set up a lot of things. Where to put end to end test.

I'm actually going to put mine in test instead of test because that's where Sinress is going to look by default. So we're going to do that. Add GitHub actions. We're going to say false. Install Playright browsers. So Playright runs a locally running browser to actually test to actually automate the testing here. And you need to install Playright browsers to get this done. So I'm going to say true. If you used Playright before, you can probably say false. But if this is the first time working with Playright, definitely say true. It's going to install different test browsers such as a test chromium, a test Firefox, etc. For us, we're going to be testing everything mainly on a Chromium type environment. But what's

really nice about Playright is it has support for different browsers. Historically, working with different browsers was kind of a huge pain in the butt. Like Internet Explorer only supported some things and Chrome and supported some things. But Playright enables us to test kind of all these different oddities between these different browsers. It'll detect what environment you're working with and download the correct browsers accordingly. Okay, cool. So once you have everything set up, you get a whole bunch of different commands that you can start running to play with Playright. So before we even get into some Sinress stuff, let's look at what we've created here. So in our folders here, we now have a test folder with an example spec.ts. ts. This is going to be an

example playwright test. Here we have some test examples which show some more examples. For now, I'm going to actually delete the test examples uh cuz we're just going to show this example.spec.ts. If we scroll down in here, we also have a playwright.config.ts. So, this is the configuration file for the playright tool. And now if I run pmppm exec playright test, you'll actually see us run the example.spec.ts and your computer. What will happen is your computer will actually spin up a little playwright browser. It'll show you the tests that it's running. So in our

example.spec.ts, we have a couple of functions in here. This looks really similar to the V test unit test except we have these new keywords like page, go to, expect, etc. Our first test has title is we have our browser go to playwright.dev and we expect the page to have title of playright. Our second test is we go to a page, we click get started and then we expect to see installation to be visible. This is a way to actually literally test working with a browser and making sure we see what we expect to see, which is really powerful. So, back in here, I can hit this play button to

run these two tests. I'll hit play, and you'll actually see the tests start to run, and you see that they both passed. What's cool is you can even kind of look in you can click on specific tests and see what renders at each step. So you can see okay when the test went to the playwright.dev site this is what was shown when it called expect to have title locator route uh this is what was shown. You can see different after hooks different before hooks. Um but this is a great way to really visualize what's going on in your tests when you actually run them. So this is a really powerful tool for us to write and run tests.

We're going to do exactly this for our Tender application using Synress. So for example, but before we do that, what if we deleted everything? We'll change this to await page.go/. And in our playwright.config, we'll define what that slash is. In this use section, we're going to define the base URL to use in actions like await page.go/. So I'm going to uncomment this line because when we run pmppm rundev, this is the URL that we're going to use.

So this is how we can get await page. This URL down here to make our test a little bit more efficient. I'm actually just going to only run this on Chromium to make this a little bit faster. And when we start up our tests here, we also want to start up our application. So, we're going to do pmppm rundev and then reuse existing server. I'm just going to set this to true for now. Okay, cool. Now, if I rerun this command here, let's see what happens.

Okay, this UI feature shows back up. It's going to load the test in here, which I've just kind of minimized. So, now we have example.spec.ts. All it's doing, remember, all it's doing in our test here is going to page slash and we're expecting to have title playright. That's obviously going to be wrong because we have a different title. But let's hit let's hit play to see what happens. So now it's running our test. And we see when we call await page.go, we can see in this really tiny little window that it does indeed pull up our website. That's what it sees. That's what the browser sees. That's awesome.

Now our test actually failed here. Uh expect to have title locator root uh because our title is different. So we go to errors. We see expected pattern playright receive string t sender. Well that's actually correct. That's what we want to see. So let's cancel that. Await expect page.2 title t sender. Let's get rid of these and just do this or let's just add tender in here. Let's clear. Let's rerun this. So now we can pop open the UI. Let's hit play to see that if this passes. And we now see it does indeed passes. So when does page go to slash, we see this little tiny little box here. We can even open this up to

show what was shown in our Chromium. And we see this is what was shown. And that is correct. And our expect passes. And this is great. And obviously if we want to rerun it without the UI popping up, we would just do pmppm exact playright test. It'll run the tests and we can run this little pmppm exec playright show report. It'll show this little report with the tests that have passed and then creates this little server. We'll hit control C to actually kill the server and move on. Okay, cool. So, we actually have a basic playright test where we make sure that our application has the title tender. Awesome.

So, this already is a good ETE test for us to have. But let's keep going. Remember, what are we actually doing? We want to make sure that when we connect, we see the form and when we disconnect, we do not. So, how do we actually set that up? Well, in here, we're going to have to code somehow connecting to MetaMask. Uh, that sounds really hard. That sounds like a really hard thing to code. So this is where Sinress actually comes into play. Sinress is going to have a lot of this code, a lot of this boiler plate to connect to MetaMask, work with wallets, etc. So let's keep going through the instructions here. So now that we've installed Playright, we've installed Synress, we want to make

sure our play rate.config.ts is correct. So let's go to our playwright.config.ts ts and let's make sure it has everything needed for us to to work here. Uh I'm going to hardcode some things for now, but when you go to run production, feel free to review my playwright.config.ts for some different updates. For workers, I'm just going to set one here. For fully parallel, I'm actually going to put false for now, but if you're using a CI, it's probably a good idea to do so. And that's it. So if you want to just copy paste mine though uh you're more than free to do that. So that looks good now. Then create a basic wallet setup file. So the way that

Sinress works is it first needs to define a MetaMask, a wallet that we're going to use. And when we run our test, that's going to be the MetaMask that it's actually going to connect for us. So we're going to do this. We're going to create a basic wallet setup file. In our test folder, we're actually going to create a new folder, one called playright, and we're going to take our example.spec.ts, and put it in there. Actually going to rename it to basic.spec.ts. And we're going to create another file or another folder called wallet setup. And in here, we're going to create basic.etup.ts. And this is going to be our basic wallet setup. If you want to

have more complicated wallet setups, this wallet setup folder is where you would do so. Now, um if you uh now we're going to copy what's in the docs, but what we're going to use might be a little bit different uh than what's in the docs. So, this is good. So, this is pretty good. Um ideally, these are environment variables. It's um it's not great to have them in plain text here, but it's not the end of the world. But this is essentially our wallet setup, which is fine. And when Simpress connects a MetaMask to our app, it's going to connect a MetaMask with this seed phrase and this password. So if we wanted it to default load up with the seed phrase from Anvil, we obviously

would use the Anvil seed phrase here, which I think this actually might be the Anvil seed phrase. Test, test, test. Oh, so that is the Anvil seed phrase. Okay, perfect. Now, the next thing it's telling us to do is create a test file. So we already have a test file, but we're going to update it with some of this Sinress stuff. So in our basic spec.ts, let's update this to our test that we want to run, right? We want to make sure when we're disconnected, we don't show the form and when we are connected, we show the form. Let's create a new test says should show the airdrop form when connected otherwise not. Do a comma async function arrow like this. This is going to be the basic

setup. Uh this is some classic JavaScript testing here where you say test kind of the name of the test and it's an async function and the details of the function are in here. So in this actual function we want to first check we see please connect wallet. So the first thing we can do is we can check for that. Um whenever we call one of these functions we want to add some different keywords such as page. So I'm going to pass in the page to this function and I can do await expect page.get by text. This allows me to find any part of the page by text. Uh please connect to be visible. So when we start

up the site we should see please connect when we are not connected. And then to test this, I can do pnpm exec playright test. And we should see both of these pass. Oh, and we actually see an issue here. Of course, that fails because I need to go to the page first. So, let's do a wait page. Go to here. And then let's look for this. And then we can run the test like here. And it passes. Okay, great. So, we see please connect. Uh, well, we could be more specific. we do like please connect wallet dot dot dot but for now I'm just going to leave please connect here. So now we want to actually well what do we want to do?

Well we want to connect our wallet to MetaMask so that we see the form here. So how do we do that? Well this is where we're going to have to use Sinress to get set up. So first thing we're going to import this basic wallet setup here because this is how we're going to set up our wallet. So we're going to say import basic setup from do slashwallet setup slashbasic setup. We're also going to import test with sin press from at synthetics io/sinpress like this. And we're going to need to import meta mask metam mask

and metam mask fixtures from at synthetics io/sin press/ playright. So now we're going to update our test instead of pulling from playright here. We're going to delete this and we're going to use the test from sin presses because this is going to come with a lot of helpful features. So we're going to say const test test equals test with syness. And here we're going to add meta mask metam mask fixtures basic setup. And then we're going to say const expect equals test.

And we're going to we'll get rid of this for now. We could um you know we'll leave it. So what's going to happen here is this MetaMask fixtures function is going to take our basic setup file and use this to create a MetaMask. It's going to add this MetaMask fixture into our test with simpress function which we're going to get this test keyword which has a lot of this MetaMask setup already set up for us. And then of course we're going to extract the expect uh function out of this as well. Now what we can do is after we run our test to make sure it starts off as blank, we can actually connect to meta mask. So

we'll say const metam mask equals new meta mask. And then we'll have to add a lot of stuff in here. Um but this function is going to come with uh a few other things. It's going to come with a context meta mask page and extension ID and we can call new meta mask with these pieces of context meta mask page. We'll do basic setup wallet password and extension ID. So these uh objects come with our test with Simpress and they help us set up a MetaMask here. So this is just the MEMS mass object, but we

actually want to connect here. So we'll do await page.get by test id rk connect button. And what this rk connect button is if we go back to am I running this site? No. Let's run the site real quick. pmppm rundeendev. I go back to the site here. Let's uh let's disconnect. I hit inspect. Inside this body tag, I can see what this connect button looks like. It's called it's really really tiny here. Data test ID equals rk connect button. So in my test here, I can say page.get text or excuse me page.get by test ID rk connect button dot click. And

this is how I actually click that button to connect my meta mask. Now, when you click it, if I click connect wallet, we now have this section here, which is if I right click on this and hit inspect, it'll bring me to this div here. And if we look in here, we see it's called rkwallet option io MetaMask. So, we're going to click that. So, we'll do await page.get by test ID rk wallet option io metaam mask. Let's first do dot wait for state visible time out 30,000 or is that 30,000? Yep. 30,000 or visible, excuse

me. Okay. So, first we want to wait for that thing to show up, right? Cuz when we click it, it takes a second for it to pop up. So, we wait to for it to be visible. Then we'll do await. We'll do the exact same thing, but we'll click that that thing which is the RKW wallet option io MetaMask. So, we'll click that to connect to MetaMask. And then we'll call await MetaMask.connect to DAP. And this is kind of the key functionality that Sinress gives us out of the box. This connect to DAP will spin up a MetaMask for us. And it will it'll spin up a MetaMask for us based off of our basic setup. and it'll connect. So now I can

run pmppm exec playright test. I'm going to do d-ui again so I can actually see it with the UI here. Make sure it's actually doing what I want it to do. And now we can see we have the our two tests that haven't run. Let's just run our second test here. We'll hit play. And we get this error cache for this does not exist. Create it first. Um this is one of the gimmicky things with Synress uh that I haven't shown you yet. So, first we actually need to do pmppm syness to create a cache. And it says building a cache. Uh, that's because somehow I added a space in here.

Uh, whoops. Let's try that again. CLI is an alpha, so expect breaking changes. Uh, building the cache for wallet setup. And what this is going to do, it's going to download the MetaMask extension, and MetaMask is actually going to pop up. It's going to create a password. It's going to basically create a wallet for us. It's going to save it to a little. Now, that error that we ran into, if we actually run our exec again, we're actually going to run into that error again. We're going to get a little Well, as of today, we get this error. Uh because I Well, I first need to go to my playwright.config, and I need to change this. Get rid of that space. Whoops. Um let's run the test again. Can't believe I put a space in there. That's so

bizarre. Now run this again. We're still going to get that error. Going to say, hey, no cache does not exist. Create it first. We just copy this cache name here. And we go up to our cache and we just rename this to what it expects for our test. So that's a little bit of the gimmick for the application right now. I expect in the future that to be a much simpler, smoother process. But now we can run this one more time and we'll get the UI to pop up and we'll get all of our tests to be in here. What we can do now is we can run this and we will see it running. We'll see it even pop up.

We'll see the password get put in and we'll see it connect our wallet and it's kind of going crazy cuz everything's being automated. Um but oh my goodness um all this crazy stuff happened and we can see kind of the flow of all the windows being open. So first it went to the extension popped open MetaMask here. So let's uh pull this down here. It went to the main page. It expected the text to be visible, which it did see. It pressed the connect button. The module popped open. And I know this is super super small cuz there's so much text here. It went to the MetaMask module.

Then it loaded up after a whole bunch of calls. It loaded up the MetaMask extension, put in the password, and connected to MetaMask through the app. And then finally all the way down at the bottom. That was it because I forgot to check to see that uh we're actually connected here. Whoops. So now what we can do, let's go ahead and kill the the UI here. Now we can do await page.get by roll. Let's look for just any text box, comma, with name ox exec. Uh we could say true. Why not? What are our placeholders? Let's go back to our airdrop form. Airdrop form. Let's look

for placeholder ox. Okay, cool. So, we'll just look for in a text box. Look for ox in there. Dot wait for state visible timeout uh 30,000. So, we're basically expecting to see one text box with this in there. We could check for all of our other placeholders as well, but let's at least just do this one. And we'll run this. We'll get our UI to get spun up. And I'm going to hit play and windows are going to pop open all over the place for me. And they should for you, too. Yeah. Yep. They're going crazy. You can't even see some of them.

But it's automatically connecting, automatically setting up MetaMask for us, which is super exciting. Automatically clicking buttons and automatically checking to see if it sees what we expect it to see. 30,000 is probably too long. Oh, and it actually failed here. So, it didn't quite finish opening. Ah, we need to finish adding a network in here. So, let's actually do that, too. So, we'll do connect adap. We'll do const custom network equals this will be name anvil RPC URL. It's going to be http127.0.0.18545.

chain ID 31337 symbol will be ETH and then await MetaMask add network custom network and now let's try this again this time in our terminal we'll first do pmppm anvil spin up an anvil chain now let's do our test here and it should work this time let's run our airdrop test and we're going to see windows fly open all over the place connected to MetaMask ask adding MetaMask. We're also going to add a new network. We're going to connect, add a network, change to the Anvil chain, and then we're going to finally check to see that we have that section

added. God, I'm an idiot. And I'm an idiot because this isn't the name of it. It's not ox. Sorry. Um, we're going to do what are we looking for? My mistake. We're looking for token address, recipients, amounts, any of these to be visible. So, let's look for token address. Sorry about that. Instead of this, apologies. We'll do await expect. That's what we're looking for. Page.get by text token address to be visible. That's what we're expecting, not the weird placeholder thing. So, we don't have to add this custom network, but in the future, if we want to do cool stuff with Anvil, uh, we would obviously need to add Anvil. So, let's run this one more time with our Anvil node running,

and let's see what we get. We'll play here on the UI and everything's running, running, running connected to Anvil. Now, it's going to finally be looking for that page. And we see a check mark here because it did indeed pass. And that's so exciting. We can see down on the tiny little box here, we can see the finalize UI connected to MetaMask. And of course, we can run that again without the UI as kind of like a regular test by just removing the d- UI bit. And if we do have Anvil uh bit set up, and if you do have the Anvil bit set up, you'll want to have your Anvil node running.

You'll still have kind of metam mask pop up and go a little bit crazy like like what we're seeing here. And now we do indeed see two past. So this is kind of an introductory to this this thing called Playright and Synress, but it is a really cool way for us to work with MetaMask and do more complicated things like connected to MetaMask. And then we can go even farther like sending transactions, doing approvals and making sure you know we send the right information and we see different spinners and you know the correct stuff show up for the user when certain inputs are set up. So when we're making more advanced full stack applications, we want to be testing end to end and this

is how we can do it. Use something like Synress, something like Playright which downloads the Metamas extension. It sets up a cache for us etc. All of that is incredibly helpful. Now, we can of course in our getit ignore, we probably want to add uh cache syness. So, we'll add syness uh cache syness. This way, we don't accidentally push up our syness cache to our GitHub repos. But this is exciting. So, we learned how to do some ET testing. When we connect, we see the form. We just tested that. And when we disconnect, we don't. Right? That was in our basic.spec here. We first we do not uh first we see please connect then we

connect with all this code and then we see the token address. And we could add more details like recipients amounts blah blah blah. Exciting. All right. Great. So we did ET testing here. And now the final thing possibly one of the most important things is we want to deploy this. So once we're happy with our website, which uh especially for hackathons, you really want to deploy your demos so that judges can actually interact with it and play with it, you're going to want to deploy it. Now, there's lots of ways to deploy full stack applications, and I'm just going to show you one of many. We're going to be working with Fleek.

Fleek.xyz. Now, the reason I like Fleek so much is that Fleek can create static sites for you. It has a ton of Web3 plugins and it has something called Fleek Functions which enable us to do even cooler things than what we're going to show you here. So, we're going to use Fleek to deploy our site. And we're actually going to make one more change to our codebase so that we can deploy it as a static site because as of right now, we're actually not quite a static site. So a static site is going to be a site that can be 100% on the browser on the user's end. Whereas a dynamic site will be a site that has a server that does some rendering or does some compute etc. For a lot of web 3 applications, we

want them to be 100% static, 100% on the client side because that's the web 3 way, right? Open source, nothing running on a closed source server. Have the user do everything. And that can be really nice for us as developers and infrastructure creators because if it's a static site, well, we don't really need to run a server that does anything other than just, you know, give people the HTML and JS. Once again, work with your AI buddies to learn more. But basically, deploying your site statically is something that a lot of applications want to do and are really good and are incredibly minimal.

However, in our next section, we will show you some really nice add-ons where you have a server which adds some really nice quality of life features, but obviously now you have to run a server. Now you have some additional infrastructure costs for running a website. But like I said, for what we're doing here, 100% static, very low infrastructure costs, and we're going to use Fleek to actually do all the deployment. So, in order for us to first get started working with, we need to sign up or login. You can use a wallet or an email. I'm going to log in with an email. Once you've signed up or logged in, we want to go over to the dashboard.

And this is going to be our dashboard for our first project. You can also hit create project to do whatever you want to do here. And we're going to go to the hosting site. And we're going to be adding a new site in here. Before we actually deploy to Fleek, we want to make sure that our code can even compile to being a static site. So, what we want to do is we want to go to our next.config.ts. ts and we want to set output to be export. This means that when we build our application, it'll build it as a static site. Additionally, we can add some other options. I'm going to add dist dur is going to be out. So that's the name of the place it'll compile to.

And then we'll do images unoptimized to be true because optimized images only work on the server and we don't want any server running stuff. We'll add a base path of blank asset prefix slash and then finally trailing slash to be true. Uh because this is something that Fleek is going to be looking for export default next config or module.exports equals next config either way. And then we want to run pmppm next build. And if this successfully builds as a static site, then we know that Fleek will be able to build it as a static site as well. So, we'll run this and we'll wait

a little bit. And great, finalizing page optimization pre-rendered as static content. That is exactly what we're looking for. Okay, cool. So, now we can come to Fleek and we can go add new um add new deploy my site. And to deploy our site, you're going to have to connect and deploy to a code location. As of recording, they only show GitHub. So, we're going to have to connect this to GitHub. And I am so happy that Fleek forces you to connect to GitHub because you should be pushing your code up to GitHub anyways. Why? Because version control is incredibly important and because GitHub is going to be your portfolio where you're going to be showing off your projects and how badass of a developer that you actually are.

And for deploying your sites, you're going to need to have a GitHub as well. So, if you don't have a GitHub, now's the time to create a GitHub and deploy your site to it. Now, if you've never used GitHub before, AIS are phenomenal at working with Git and GitHub, and I highly recommend you learn how to how to upload your site from your Git. So, we can go to github.com. I'm already signed in, but for me, I'm going to hit new. I'm going to call it tst sender UI. CU. We're going to make this public. Create repository. And now I have this new repo here. And I'm going to push my code up.

Get add remote origin. Paste this link in here. Oh, first in my codebase, I'm going to do get initialize a repository. And you'll see a whole bunch of green stuff show up in here. Then we can run git status. And we can see which files that we would push up. We want to just run through these and make sure that these are the files we want to push to GitHub. Remember, this is what we're going to be pushing publicly. If you've never worked with GitHub before, be sure to ask your AI lots of questions. Yes, I want to add this. Yes, I want to add this. Yes. Yes. Yes. Yes. Yes. Yes. Yes. Yes. Yes. Yes.

Yes. What we want to make sure is not in there is env.local. Any of this cache simpress.next node modules out play report test results env.d.ts ts. I don't see any of those in the output here. So great. So what I'll do is I'll do get add period get status. Now I'll see a ton of green files. Uh these are going to be all of the files that I'm going to push up to GitHub that I'm going to push publicly. They look good to me. So I'll do get commit minusm my first commit. Enter. Then I'll do git remote add origin and I'll copy the link

of the repo that I just created. paste it here. Then I'll do git git push origin setup stream origin main. And if you're not signed in, you might have to sign in. You could download the GitHub CLI to do this. You could work with git directly to do this. There's lots of different ways to do this. I'm going to cheat a little bit. I cheated a little bit and I pushed it up offstream. But now if we go here, I can see all of my code up on GitHub, which is incredibly exciting. Cool. And if this is your first time pushing up to GitHub, I know I didn't show you step by step here, but you should be pumped about yourself.

Well, you should copy paste this and post it on Twitter that you posted to GitHub for your first time. You're actually becoming a developer. You're actually becoming an engineer. But actually, you know what? Never mind. Don't celebrate yet. Let's deploy the site and then you can celebrate. So, we're going to select GitHub as our option here. And resources on your account. All it does is view our email. And Fleek is asking to get access to our GitHub. All it's going to do is be able to read our email address. So, we're going to go ahead and authorize that. Authentication complete. Okay, great. So, now it's going to ask us to install the Fleek app on GitHub. We're going to add it to our GitHub. And we want to be a little bit more secure. We're going to say only select repositories. And we're

going to get that TTS Tender UI safe and updraft. Install and authorize authentication complete. This is going to add some GitHub actions to our GitHub. Now, it's going to say, do you want to deploy this site? We're going to say, yeah, we want to deploy this site. It's autodetecting. It's a Nex.js site. And this is where we can add this is where we can add the key features here. So yes, we want to be on the main branch. Yes, we want to look in the out directory. And then let's look at the build command. We can copy paste it in our repo to make sure that it is the correct command that it's running

here. pmpp install and pmppm build. PMP install and pmpp run bold. Those look correct to me. I'm able to create a static site locally. So okay, those look right. Let's show advanced options just in case. Base directory looks fine. We don't and we do need some environment variables actually. So let's grab our environment variable from ourv.local and the project ID here. Paste it in. We'll hit add. We'll add it in here. And then we'll go ahead and deploy the site. And that's it. I hope now we can go to our site. And we can see that our deployment is indeed building. So it's actually building a website for us, for us to view, and for

us to hang out with. If you go to the repository you created and you're fast enough here, you might see a little little yellow orange dot here where it's saying Fleek is building our site, which is very exciting. We can go to the deployments tab and we can kind of check on how this is doing by looking at the logs here. So, it successfully cloned the repo. It did a little check run. Now, it's building the logs and now it's actually building the site. So it's running that pmppm next or pmppm run build here. Then it'll upload to IPFS, check the availability of the domain and then release it and we will have a website deployed automatically for us through Fleek and it'll be uploaded to

IPFS. Now IPFS is a phenomenal hosting platform for 100% static sites. It's not going to be something I go over too deeply here, but I highly recommend you check out IPFS and you learn about IPFS because it is a very powerful tool for deploying static sites with decentralized frontends. Now, if you run into an error here and quite often you'll see something like this directory does not exist like what you see here, this is kind of the default error that Fleek will give you on the UI and this basically just means that something happened in the build step that failed and it was unable to deploy. So I found a lot more success with instead of using UI, you use the command line for fleek instead. So let's go ahead and let's use

the command line. So actually on my dashboard we have mpm install-g atfleek platform/I. I'm actually going to copy this and run this on my terminal. And this will install the Fleek CLI. This will allow us to actually deploy our sites directly from our code terminals. And this is in my opinion a lot better because we can actually test exactly what is going to be compiled and deployed etc. And to make sure everything works like I said I found fleek is a little bit happier with npm. So we want to just make sure that mpm install works correctly. And then we also want to make sure that mpm run build or mpx next build works correctly.

Something important to note is in our tsconfig.json, we want to make sure that everything in the test folder. So we can do something like this. Everything in the test folder is actually ignored. This way when we do a build of our next project, it'll not add anything in the test folder, which is what we want. So it looks like our mpm install worked. Then we can do mpm run build or mpx next build. And if this creates a static site successfully, then we should be able to use the CLI to also deploy a static site. All right, great. We see static here. So this means we should be able to deploy our static site with Fleek. So now we can use the Fleek CLI to deploy

our site. The first thing that we're going to do is we're going to need to log in. So the Fleek CLI is really easy to log in. We just do fleek login. We get to click this link or copy paste this link and we can go ahead and confirm and we can visit the dashboard or we can go back to our CLI. It says success you are now logged into the fleek platform. Now we can run fleek sites anit says warning to proceed. Please select a project and I already have a first project created. So that's what we're going to be using here. The first project we found existing sites. Would you like to link to one of them? I'm going to say no. We're going to create a brand new site. I'm going to call this Tender 2. You can call it whatever you

want. Tender 2 has been successfully created. Please specify the directory containing the site files to be uploaded. So we want to upload the out directory because this is the compiled code. This is the static sitebuilt code. So we could dot /out like that. Would you like to include an optional build command? I'm going to say yes. MPM run build is great. I'm going to use the JSON. I've found the most success with the JSON. And then we'll do clear. And if we open that fleek.config.json, we see we're given a random slug for a URL, the dist directory, which is our out folder, and then the build command. Now that we have that, we can just do fleek sites deploy.

And it'll build our application locally and then deploy it. And this is why I like doing this from the CLI instead of the UI is because we get to build it locally and we get to see exactly what's happened a lot easier. Once you get to this part, you should see some success here and then it'll upload this to IPFS and give you a URL for application. Now, this might take some time. So feel free to maybe go get a coffee, go get some ice cream, go for a quick walk because this can take you know 5 to 10 minutes to actually finish deploying or actually mine deployed super quickly and we get this output here if we're successful. Uh it was deployed to IPFS and we can see the site by commandclicking this or open

this in a URL and oh my goodness, our site is actually deployed to a website. Let's try to connect our wallet here with MetaMask. And oh my goodness, it actually connects. It actually is connected to our Anvil. So, hold on. Let's do a little PMP Anvil. See if we can do our our goody stuff here. Uh we are connected. Let's uh connect. Let's change our accounts to our account 8. Um let's add our address here. Let's add a different address here. Let's say 100.

Let's do send tokens. Let's inspect. See what's going wrong here. Inspect. Oh, we're getting an error here. Uh, we need to turn our brave shields off for this. Let's try again. Do 100 to send tokens. Our MetaMask does indeed pop up. Blockade is blocking it, which is good, but we're going to ignore you. Confirm. We get a console log with the approval amount. Okay, this pops back up. Let's confirm again. Yes. Confirm. Okay. So, now let's grab this token address. Let's pop it into our new account. Tokens. Uh, import tokens. Paste this here. Oh. Oh. Oh, we

can see we were given the tokens on this contract address. It's like 0.00001 because we only sent 100 and it has 18 decimals. But awesome. We actually deployed our website using Fleek using the CLI. And now we have a 100% static site deployed using Fleek. And it was deployed instantly all through our command line, which was much easier to use. And this is a static site deployed to IPFS as well. And if we go back to our Fleek UI and we do a little refresh now for our sites, there should now be a Tender 2. Yep, we see that here. We can see a little preview here.

We can see it is deployed and it is ready. We can see it is deployed to IPFS and everything about this site. So Fleek has made it really easy to automatically deploy our site IPFS and URL automatically. Now if you want to go one step further, you can also attach a domain like for example tender.com obviously and not kind of this wonky uh you know hissing pedabyte square blah blah blah. it is at this much nicer domain and like I said this has a lot of the quality of life improvements so it is a it's much more responsive it's nicer to read than uh our kind of growth site here but this is super exciting you have deployed a website with web 3 with blockchain functionality you should be

incredibly proud of yourself and if you want to go so far to make an even cooler website than the T please do so then send me a link because if your website is cooler than my website well uh I want to use it cuz Like I said, the Tender site is one of the sites that is actually used to do code hawks payouts. Well, I should say more specifically, the Tender codebase is what's used to do code hawks payouts. Uh, and sometimes the Tender UI is used, sometimes it is not. But in any case, this is incredibly exciting because guess what? You just did the final step in our section two here. You have deployed to Fleek. This is fantastic.

Right. This is so exciting. You should be so so proud of yourself. So, if you haven't been following along with me, guess what? Your next challenge is to do exactly what we just did. Deploy your own site to Fleek. Now, you don't even have to deploy the Tender codebase that we just said. Just deploy something to Fleek. You can use the CLI, you can use the website, whatever you want. This is going to deploy to IPFS, give you a URL. Once you do that, you should come back here and click this link. This is a tweet generator link. It'll automatically create some text for you.

I just deployed an XJS site using Fleek that can connect to ZK sync. Thanks Patrick Alpha and Siphon Updraft. And hit post. Let us know that you've done this cuz this is incredibly exciting and you should be super super proud of yourself. So, so please. Now, like I said, doing these challenges is incredibly important and getting your first website up is going to be incredibly helpful for your confidence and for your portfolio. So definitely be sure to deploy this to Fleek. Definitely be sure to tweet this out at us. The Fleek team and the ZKSync team, they love seeing developers thrive in this ecosystem. They love seeing developers thrive and build really cool things. And maybe you'll find some friends along the way. So be sure to celebrate the wins.

Be sure to actually deploy this site and then tweet it out. So pause the video, make sure you get this done, and I'll see you soon. Welcome back. Hopefully, you deployed your site because that is what's going to give you the skills here. Repetition is the mother of skill and we want to make you incredibly skillful. So, we need to repeat this. We need to have you deploy a lot of sites so you get really comfortable deploying these sites and working with these sites. Hopefully, you actually did the challenges above here as well where you added in your own quality of life features and hopefully now you have a successful website up deployed to IPFS and you are incredibly proud of yourself and we are incredibly happy with you.

So, in this section, we learned a lot. There was a lot of stuff that we went over in a very short amount of time, and I had you use AI a lot to substitute some of the learnings in here because there's a lot of full stack learnings that we kind of skipped over here so that we could focus purely on the web 3 aspects. So, let's go over all the things that we learned here in a quick review and then we'll finally go to the final section. So, first of all, we learned how to build a more productionready codebase using Next.js.

Why did we use Next.js? Why did we use React? Well, it allows us to build more modular websites that have both JavaScript and HTML combined or in our case, TypeScript. We first got started with our Rainbow Kit, which allowed us to build a really, really pretty connect button on our website. It allowed us to very easily switch between supported networks and automatically have our wallets update to those nicer networks. We learned we could create little customizable components like what we saw here. We made a header component and a airdrop form component. We created this giant component, this airdrop form which used a ton of react hooks and used a lot

of these WagMe functionalities such as read contract, wait for transaction receipt, use chain ID, use config, use account, use write contract for us to actually interact with these blockchain applications. We created a little flow here for us to both approve our contract using write contract async using the await keyword and then contract async as well to actually call the airdrop earc20. We used AI to help us make a nice little input field so that we didn't have to make it. And we learned that especially for kind of these dull things like creating a little input field AI can really accelerate our development journey here. We learned that we can create different utils and we can create tests for them right

alongside the main files. And if we want to actually test them, we can run pmppm run test unit like this. And we can test our functionality to make sure it does what we want it to do. Not only did we learn how to write some unit tests for our codebase, but then we also learned how to run endto-end tests with playright and syness. For us to write a simpress end to end test, we could do pmppm run test or pmppm exec playright test. And since we're using syness, this automatically had us install a MetaMask instance into our test environment. So we could test what it's like to connect

to MetaMask, work with MetaMask, etc. We didn't go super deep into working with Sinress, but if you want to go deeper throughout the documentation to learn more, you can absolutely do. So one of the coolest things about Playright is that Oh, and I actually just ran it and I ran into an error because I didn't have Anvil running. Um, but if you want to go deeper onto working with Playright, working with Synress, I highly recommend you do so because like we said, you really want to make sure that your code actually works. when you're building these full stack applications. So, we created a basic Nex.js React application. We used the rainbow kit to make our connect button much prettier and much smarter and much more functional. We implemented our Airdrop ERC20 contract using Wagi React hooks.

We did some endto-end testing. And then finally, we used the Fleek CLI to deploy our application to an IPFS instance and have a website right here. We could have also used the UI, but we ran into some issues here. So, if you want to go back and try to figure out how to get the UI to work, great. Go for it. But like we said, when we build locally with the CLI, we can see directly what's going on to make sure that everything works perfectly and we get our site up. So, this was an absolutely jam-packed section and kind of a huge jump from the HTML TypeScript section to this React Next.j section. There is a lot to learn here and AIs are really helpful at getting a lot of the basics. Like I

said, like AI was actually able to generate a lot of the code that we wrote here for us, which was great. We want to use AI. We want AI to be our companion, but we cannot trust it 100%. So the siphon team, the team that I work on, we get auditors, we get smart contract developers who send us AI generated code pretty often and it's kind of a nightmare for us because the amount of bugs that we find in this AI generated smart contract code is a lot. Like I said, especially for smart contract code, if you mess something up, you could be out $1.5 billion. Or even on your front end, if you have a security vulnerability, same thing. And we do not want to have security vulnerabilities. So we whenever

So the biggest thing I want you to take away from this is whenever you work with AI, whenever AI gives you some code, you should be going through it line by line making sure that it's not giving you anything dumb. If you do not understand a line of code that the AI gives you, you should not use it. Make sure you understand everything that's going on. Treat your AI companions like kind of a stupid college student who will just, yeah, figure something out and give you the codebase, but they're not the experts. you are the expert. So, like I've been saying, repetition is the mother of skill, and we want you to be incredibly skillful. So, we will have you do a lot of repetition. The more you repeat this, the better you'll get and the better you'll get at very quickly understanding if the AI is telling you

something stupid or telling you something intelligent. So, with this being said, huge congratulations on finishing this section. Now's a great time to go get some ice cream, go get some coffee, go for a walk, and I'll see you in the next one. All right, welcome to the final section of our full stack AI powered vibe coding journey here. The code we're going to be working with as always is going to be in the GitHub repo associated with this course. We can scroll down. We see some

nice little images of the site and we can view all the code in this repo here. Now, unlike a lot of the last projects that we did where we started them from scratch. This codebase actually has two branches. Well, it might have more branches by the time you watch it, but in particular, it has a starting code branch and a main branch. The main branch is going to be the finalized completed version of the code and the starting branch is going to be where you are going to be starting building the application. AI can do a great job building a lot of boilerplate and defaults. So what you're often going to be faced with is making improvements to existing code bases. And this is where your knowledge of different architecture and solutions is going to make your life

a lot better. So for this project, we're actually going to start with some code already built and we're going to have you implement a couple of features. Unlike our last project where we built a completely static site, for this one we're going to be building a more dynamic site. We're going to have a server. We're going to have API calls we're going to need to make. And we're going to add a lot of different features. The two features in particular that I'm excited to teach you about are going to be the indexing feature and then also the compliance engine. But before we get into all that, let's go ahead and let's take a look at what the final project is going to look like. So, we're going to be making a very minimal NFT marketplace. Now, originally, I was going to turn this into an AI NFT

marketplace, but maybe I'll have that as a challenge for you to implement for this codebase. As you can see, it's pretty minimal. As you can see here, we have a little header at the top with a similar set of buttons as we had in our last project. We are once again connected to the Anvil chain here. And what we can see is we can see a list of NFTs here listed with contract addresses, token IDs, and prices for the NFTs. If you click on one of the NFTs and you were the seller, it'll tell you you're the seller. But if we connect with a different account, it'll bring us to the page to actually purchase the NFT from the marketplace. We can also list an NFT by passing in an NFT contract,

token ID, and a price. We also have this cake NFT um section which allows us to bake a cake NFT, which is a minimized pseudo random NFT where the cakes colors are pseudo random. And what we can do on this page is we can view what the different cakes look like. So if we put in token ID zero, we see this here. We put in token ID 1, we put in token ID 2, token ID 3, they're slightly different cakes. Now most of the functionality of our So this is what our final project is going to look like. Then once we've created a cake or some other NFT, we can list it, paste in the contract address, the token ID, and add a price of USDC.

Preview listing. We would first approve it to be listed. I'll go ahead and confirm. And once it's confirmed, we can actually list the NFT for sale. And we'll get a success. Your NFT has been successfully listed on the marketplace. We can go back to the homepage. And now we can see token ID number five has been successfully listed and is available on the homepage of the NFT marketplace to be purchased. So this is what the final code looks like. Let's look at what the starting code looks like. Looking at our starting code base, everything is pretty much the same. We have the cake NFT bakery. Here we have a way to list and sell NFTs. However, the set of recently listed NFTs just shows this blank here.

How are we actually going to get all of the recently listed NFTts into our application? We'll be looking into the Solidity codebase to figure out, well, okay, what's the best data structure and what's the best way to read from these contracts? Additionally, we're going to add one other feature here. To do this, we're going to learn about something called indexers. But we're also going to add one other thing to this codebase. If we decide that we want to buy an NFT, most addresses are going to be able to get to that purchasing page. However, maybe we've decided that hm our application is centralized enough that we actually want to start checking for hackers or malicious users and blocking them from our website. So what we can do is we can add compliance APIs in order

to keep the regulators at bay and try to prevent any abuse of people working with our website. Now of course this will not prevent people from interacting with our smart contracts. But if a regulator comes by and says, "Hey, what have you been doing to prevent hackers from doing nasty things?" You can say, "Well, I'm using this compliance API in order to keep the hackers away." But because the smart contract is deployed on chain, there's nothing I can do if they interact with that. So these are going to be the two main pieces of functionality that we're going to be adding to this codebase in this final section of the full stack crash course.

So with that being said, let's go ahead and let's get started. Here we are back in our course home directory and we're going to go ahead and make a new folder MKD. We'll call it TS NFT marketplace siphon updraft and then we'll cd into this directory here. Now we're going to want to open it with code dot or of course you can do file open folder to open this up in a brand new window here. Now for us unlike some of our last projects we're actually going to start by cloning this repository. So we're going to copy the URL here. Go back to our

tournamental and we'll do get clone paste this in. We'll do get clone paste this in. I want to stay in my same folder. So I'm also going to put a space and a period here. So it clones it into my current directory. And we have all of our starting code set up here. We want to make sure that we're on the starting code branch. So we'll do a little get branch in our terminal. And we do indeed see we're on the starting code branch. The starting code is going to be the default branch of this GitHub repo. So, this is where we should actually end up starting. Cool. So, let's take a little bit of inventory through this project, see what we have here, and then we'll start the project up, see what it actually looks like on the website, and

we can make our readme and our game plan of the improvements that we're going to make. So, first off, we have this Foundry folder. And for those of you who are smart contract developers, this will be pretty familiar to you. This is a Foundry codebase with Solidity smart contracts. In particular, if you go to the SRC folder and yes, you'll get some red squiggly lines here because we haven't installed any dependencies, but we can see we have the cake NFT, which is going to be one of the main NFTs that we sell on the site. We have a mock USDC because the token that we're going to be using to purchase NFTs on the marketplace is going to be USDC or some type of ERC20. We have a mood NFT, which if you've taken the solidity curriculum

in Cipher Updraft, this will look pretty familiar to you. It's a NFT that allows you to flip the mood. And then finally, we have the NFT marketplace codebase, which is going to be the codebase for the actual marketplace. If you peruse some of the functions in here, you'll see a lot of the main functionality that we're going to be building or that is already built on the website. There's a list item function which will allow you to list an NFT on the marketplace. Cancel listing which will remove an item from the marketplace. You have buy item which of course will purchase the item from the marketplace. Update listing which updates the price and then withdraw proceeds which will allow

people who are selling NFTs to actually withdraw the money that they've gotten from selling the NFTs. And then we have some getters down here. It's important to actually go through this because what's going to become very clear to us looking at these getter functions, we can already start to see a little bit of a problem here. If we want to get all of the currently active listings, how would we do that with these getter functions? Well, we could call get listing for every single token ID and check to see if it's active or not. How would we check to see if a token ID is active or not? Uh, well, there's no function for us to call. So, uh, that's a little odd.

There's no is active function. We could call get listing for every single token ID and see if the S listing returns a listing that is currently active. So, listing has a price and it has a seller. So, if the seller is not the zero address, we could do that. But that might be a ton of API calls. Is there anything else we could call? Not really. Oh, yikes. Okay, so we wouldn't if we had tens of thousands of different NFTts, we'd have to make a ton of API calls. So, that's not something that's going to be realistic. So, we'll come back to this and figure out how to solve that problem a little bit later.

Obviously, we have some tests, some foundryl, and this is just all the codebase that is used to create the smart contracts for the site. Then we have a public folder which just has some images for our website. And we have the src folder which looks pretty familiar to what we did in our last project. We have a rainbow kit config for working with a wallet. We have a constants file which has a number of addresses for the different NFTts in our project. We have utils for formatting some pricing information. We have some different components like a cake NFT, a header, the list form box, NFT box, recently listed, an input field similar to last

project, and then we have our app folder which has a lot more Nex.js setup in here. So, we're going to be working with a Nex.js and React project again. Then we have a lot of configuration files and importantly, we have a marketplace-an.json, JSON which is going to be the JSON file that we can load into our anvil instance which is going to have everything already created for us. It's going to have the NFT marketplace smart contract deployed the cake NFT mark the cake NFT deployed the mockuous DC token deployed and the like. So for us to get started at least looking at what the site looks like now let's go ahead and run pmppm install and let's look at our

package.json to see what scripts it comes with. Okay, it comes with an anvil script here that we're going to be using and then obviously a dev script as well. Okay, so once we've installed everything, let's do pmppm rundev. And let's also create a new terminal and run pmppm anvil in here to start up an anvil node using the marketplace-an.json. Now that we have both of those running, let's pop open this local host up. And it looks like we have an error. No project ID found. That makes a lot of sense cuz what else do we need? Well, we need av file or env.local with some environment variables. I believe the

site has it. Uh we need a next public wallet connect project ID. We can skip this for now. We can skip this for now. And we can skip this for now. But we at least need this. So I'm going to paste this in here. And I'm actually just going to borrow the same one that we did from the last project. tst senders UI cipher updraft. Do cat.loc. Great. This is what we're going to use for here without that piece. Let's restart this. Now that we have that, go back to the front end. Refresh.

And okay, we can see it actually running here. Great. I'm going to go ahead and connect to my Anvil One account that I've added to my MetaMask here. All right, cool. In my MetaMask now, if you go to the token section, if we go to our constants.ts, we can see the different addresses of some of the different tokens. Let's grab this USDC address and add it to our tokens. So, I'm going to do import tokens. Paste this in here. says it's USDC and it says the anvil zero address starts with 200 USDC. Let's uh expand the view here. So we have 200 USDC. And again, this is the

ox39 account. You can see that in in your PM PMP anvil instance that's running. If you scroll up to where it started, this is going to be account zero with private key of zero. So, that one's going to start with 200 of those. We can look at the cake NFT. And in our MetaMask, go to NFTts. We'll select import NFT. We'll paste it in. We'll add token ID zero. We'll select import. NFT was successfully added. And uh MetaMask doesn't do super great with B 64 encoded NFTts, which is too bad. It really should do better um with that. So, it

doesn't show the images. That's too bad. Metamas doesn't do great with B 64 encoded NFTs. That's too bad. Uh, so they're all going to show up as blank, but that's fine. They're in there. So, we have some NFTs. We have some tokens. If we wanted to list an NFD, we could list an NFT. We wanted to bake a cake. We could come here. We could bake a cake. Um, so let's go ahead. Let's bake a cake. We get our MetaMask pops up saying do you want to call this function which I'm going to tell you this is the bake cake function. Let's go ahead and confirm. It'll bake our cake and it'll say token ID five minted and then we could pop five in the cake viewer and we can see what the different cakes look like. Now the issue obviously is let's

go ahead and list that NFT. So this is the cake NFT that is token ID 5. Let's say it's $100. We'll do preview listing. We'll approve the NFT. We need to confirm approving it for the marketplace first and then we need to list the NFT for sale. Once it goes through, let's confirm that as well. And your NFT has been successfully listed on the marketplace. But if we go to the marketplace home, nothing shows up. So, how do we get the different NFTs to actually show up on the marketplace? Now, I told you that we're going to be using indexers, and that's going to be the answer to this question. However, I want you to try yourself to take some time to figure out, okay, well, what might you do to have all the NFTs listed

here? You can 100% use AI. And in fact, I'm going to show you what DeepS might tell us. And then I'm going to tell you why. Again, this is why it's so important for you to be good at what you're doing because DeepSeek is going to give us something that I don't really like. So, pause the video, take some time, figure out what you're going to do. Then we're gonna ask Deepseek and then I'll show you the answer and I'll show you why it's such a hard problem. All right, welcome back. Hope you took some time to look into this and I hope you did ask your AIS because I'm willing to bet that they're going to give you not quite what I want them to give you. However, maybe by the time this comes out, they're going to go ahead and crawl siphon updraft, which

they often do, and add this to their AIS, and maybe they will suggest what we're going to suggest that you do here. But here was the prompt that I gave our our AI to hopefully facilitate a good answer here. So, I said, I have an NFT marketplace site where I want the homepage to show recently listed NFTts. Here's what I have so far. Three back ticks. And I put the code from the where is it src components recently listed here the main component here. Then I said here is my NFT marketplace and I copy pasted the code from the NFT marketplace solidity. Pasted the whole thing in here. Give it lots and lots of context. And then I said and here is how I use WagMe and Nex.js in my last

project to call smart contract functions. And I just literally pasted the airdrop form from our last project in right here. Then finally, I said, "How can I get a list of the most recently listed NFTts and show them on my UI?" I have this be the Deep Think. And I went ahead and I hit enter. And we'll see what DeepS gives us. And so let's see what DeepS had to say. So Deepseek actually gave us what I kind of wanted you to run into as well. Deepc has this paragraph here says, "Wait, but the contract doesn't track the time when an NFT was listed. The events might help here. Every time an NFT is listed, an item event, the item listed event is emitted. So the front end can listen to

these events and track the listing with their timestamps. Alternatively, they can query past events to get all listings and then sort them by block number or time stamp to determine the most recent. So this is actually the correct answer. So what we want to do for our smart contract is yeah there's not going to be any function that we can call like get active listings and this is good right because in order to track the active listings the smart contract developers might have had to create a more complicated way to store listings which would have cost users more gas so it's actually good that the developers made it very minimal and very easy for them to store the NFTts but it's also good that they emitted these events so that a UI I can basically just listen to

these events and parse through and figure it out. Like for example, if this was the list of events that were omitted, an item listed, item cancelled, a front end or a database could say, "Oh, okay, cool. Contract A ID0 was listed." Let's have our active listings database and contract A ID will be in that database. Then it sees item canceled. contract a ID zero and offchain in an active listings database our front end or our database would go oh well it was removed let's just get rid of that and then finally item listed contract B ID0 that it hears that event so it adds that to the active listings database so this is one way we could

listen to events to figure out what are the currently actively listed items another way we could do this is we could just have a database that's a little bit dumber has an item listed database which would just have contract A ID 0 and contract B ID0 and then it also could have an item cancelled database which would just have contract A ID0 NFT listings it would just look at item listed database and then subtract all the ones in the item canceled and then obviously all the ones in the item bought as well and that would be the other way to get them all off the blockchain. So indexing in the blockchain world is the process of listening to these events and then

storing them in some type of data structure like a database in order to be easily queried later. Indexing is the way to read blockchain data because like we said the developers could have added you know function get active listings but then the contract would have been bigger then they would have had to store the listings in a more complicated data structure and the users would have to pay more gas and that's not good. So instead, we're going to use this indexing methodology to listen to these events being emitted, capture them in a database or some type of data structure, and then read from them. So great. So our AI was able to deduce that, okay, we should do some type of indexing

solution. So let's see what AI gave us and let's see how it didn't quite get what I wanted it to do. So its initial thought was to query the smart contract for all item listed events to get the history of listings for each event extract the NFT address token ID price seller and then do all this on the front end. This would have been a pretty bad solution. It would be very very very slow for the user's client to do all this indexing and basically when you'd load the page you would have to wait 8 years to wait for all the actively listed NFTs to come online and it actually deduces that. that it basically says, "hm, that could be really inefficient if there are a lot of events." It said, "Hm, maybe we can do a local cache of the listings." And after

kind of arguing with itself back and forth a whole lot, it decides that the suggestion that it's going to give us is just going to be to index every single event starting from block zero from all time when the user loads the page. Yikes. This is kind of a terrible solution because obviously the user is going to have to sit there and wait 8 years while their MetaMask makes a billion API calls to try to get all the events. What a lot of AIs have also suggested is other paid solutions, which paid solutions can 100% work, but I'm going to tell you now, there is a fantastic free solution that we're going to use. It's free. It's open source.

It's amazing. And that's going to be our desired solution here. I actually was surprised that DeepS didn't give us one of the paid solutions that's out there. The reason there are a lot of paid solutions that are very good and they are great to use, but the reason that I don't like them is that they don't work very well for local development. They only work for live chains, for live NFT projects, and it makes them very hard to test and they're paid when there's a perfectly good free solution that we can use. So, this is helpful, but not good enough AI. And this is where learning about other solutions makes life much better. What we're going to be doing to do this indexing is we're going to be

using this R indexer tool created by one of the developers on the A project. This tool is outstanding and you'll see why I love it so much. You can get a link to this from the GitHub repo associated with this section under the R indexer. This R indexer is going to be a tool to actually do a lot of the indexing that my AI told me to do and maybe your AI will tell you to do as well, but it's going to do it offchain in its own server that we can have an that we can make API calls to the user in order for them to get and we can have the users make API calls from their clients in order to query the data in a much easier much faster manner. So let's get started

working with Rindindixer and then we'll update our component on the front end in order to view these NFT. So we can go to the rinder.xyz and we first want to download the rinder. Downloading things can be one of the hardest things that we do. So if this takes you some time to download, no worries. Once it's downloaded, we can check it out with rindexer-help and see that we do have the rindexer command created. It comes with a number of commands. new, start, add, codegen, delete, phantom, and help. We're not going to dive into all of the features in here, but it has some really, really cool functionality, especially with the codegen functionality where if you want to add

your own Rust code to your indexer, you absolutely can. But let's go ahead and follow along with the docs to get started here. Great. Now, the next thing that you're going to need to actually work with our indexer is going to be Docker. So, if you don't have Docker installed, now is a great time to do so. Docker, like I said, is one of these other tools that can be kind of challenging to install. So, if you haven't installed Docker before, please pause the video, take some time to install Docker. We're not going to focus a lot on how to use Docker here, but it is a tool that is a requirement for us here. Once you have it installed, you can check that it's actually running with docker ps and you'll see something

like this. If you get like, oh, docker was not found, then you know that you actually haven't installed it correctly. So, or you can do docker dash or you can do docker- version. See what version of docker you've installed. Once you have those installed, we can actually go through the CLI to create a new indexing setup. So, to get started with it here, we're going to do r indexer r indexer new. And this is going to tell you to either use no code or Rust. We're going to be focusing on no code. But if you want to go crazy mode and you want to actually customize your indexing and write some custom code, you can obviously do the Rust here. So we're going to do R indexer new no code. And it's going to prompt us to help set up

our R indexer project. Project name is going to be marketplace indexer. This is going to be the name of the folder that the indexing code is going to live. project description. I'm just going to hit enter. Repository, I'm just going to hit enter. What stoages to enable? So, the way that this R indexer works is that it's going to listen to these events emitted on chain and it's going to store them like I said in some type of database. You can use as of today, you can use Postgress, CSV or both or none. I'm going to choose both for now so that if you're unfamiliar with Postgress or databases, we can at least see the data come in in a CSV file. For a production server, you're going to want to use

something like Postgress here, but I'm going to just use both for now. And Postgress Docker supports out of the box. I'm going to say yes. If you're super amazing at Postgress and databases working with them, feel free to say no here. My recommendation is for following along with the curriculum, you do use the Postgress Docker implementation because that's going to make following this tutorial a lot easier. We're going to select yes and it's going to start everything up with a template for us to get into. In your folders now, you'll see a new folder called marketplace indexer. Let me walk you through what is in this. The most important file is this

rindexer.yaml and this is the node code solution that tells rindexers what contracts and what events it should actually listen to. Then we have a docker compose. This is a file that helps spin up our Postgress database which again is going to store and keep track of all the indexed events that our contracts emit. We have ait ignore which obviously is ait ignore. We have aenv which as of today comes with some default dev environment postgress database URL and a postgress password.

Obviously for a production environment you'd want to change these to be something much better. We have a set of AIS which these are once again going to be the which are which these are going to be the AIS of the contracts that we're interacting with which are going to be able to tell our indexer what events there are, what functions there are, etc. and then and that's it. So for us, we can actually very easily update this rindexer.yamel to start listening to and listening for events on our NFT marketplace. Remember, if we go to the foundry codebase src NFT marketplace, what are the events that we're listening for? We're looking for item listed, item canceled, and item bought. And we want

our indexer to create give us a database that we can query so we can derive from these events what are the currently active NFTts so that we can show it on our marketplace. So how do we update our indexer.yml to do so. Well the beauty of this is that it is no code and all we have to do is update this YAML file and it should work great. So let's first get started updating the networks. For us, we're just going to get started working with the anvil chain on 31337. And the RPC is obviously going to be http127.0.0.18545. Again, if we just run anvil, this is this URL that gets posted

to us here. Okay. Stoages Postgress enabled true. CSV enabled true. in the path generated CSV. So in our marketplace indexer, we're going to get a generated CSV folder which is going to have all the CSV stuff. Again, for probably a production environment, you're probably not going to want this. But for this tutorial, we are going to have these printed out here because a lot of people might not be familiar with Postgress databases. Native transfers, we're going to turn this off. This is for looking at events that just send ETH. And now down here we have the list of contracts that we actually want to listen to and listen to the events. So for us, we don't want to listen to

RocketPool ETH. We want to listen to our NFT marketplace. So we copy the name of this, our NFT marketplace details. The network is going to be Anvil and the address is going to be what? Well, we gave you the address in the constants file. So, let's go back to the constants.ts. It looks like the NFT marketplace address is here. So, let's copy that. We'll paste that here. Now, the start block and the end block is going to be where do we want to start listening from? Where do we want to start listening to events? And it's really good to give a start block if you don't want your indexer to start from

zero and have to go through the entire blockchain. But for us, since we're working on a local chain, which is only going to have a few blocks, we can go ahead and start from zero. And we don't have to put an end block because we actually want our indexer to keep listening to events and keep updating accordingly. If you're just looking to maybe populate a database, maybe get some starting events, you can 100% add an end block. But for us, we want this to continue listening. So, we're just going to have start block zero here. Now for ABIS, we are only listening to NFT marketplace events. So we want to get the AI for the NFT marketplace. We can find that in our Foundry codebase. So if

you have Foundry installed, we can CD into Foundry. We can do forge build which will install all the dependencies and then we'll build the smart contracts to an out directory which will have the AIS. If you do not have Foundry installed, now is a great time to install Foundry because I don't know how you made it this far in the curriculum without having Foundry because we've been using Anvil the whole time. Um, so uh I mean I'm like impressed that you made it this far. I don't I don't know.

And one more thing, uh, the way that I built this codebase is you actually have to do forge build dash because stack too deep is a thing. I have a whole rant on stack too deep, but we'll save that for another day because this is about full stack. This is not about compilers. The summary of that conversation is have you heard of Viper? The summary of that rant is have you met Viper? Anyways, now that it's compiled in our out folder, we're going to have a list of all these contracts in here. And one of them is going to be this NFT marketplace. And if we click on the NFT marketplace and I'll do commandshiftp or just open your command pallet, we can format it to make it look a little bit nicer. And we can

look for this AI section. And in my VS Code, I get this little drop down that I can click. And so then I can just go from here to here, copy it, go back to my R um go back to my ABIS, new file, NFT marketplace, ABI.json. JSON. I think it's JSON. Yep. And then just paste that whole thing in here. Now I have the whole AI for the NFT marketplace. So now we'll call this NFT marketplace.ABI.json. And what are the events that we're looking for? Well, if we go back Oh, and then we don't have to save that. Well, if we go back to the NFC marketplace, we're looking for item

listed, item cancelled, and item bought. So, we'll say item listed, item cancelled, and then a new line here for item bought. Boom. And that's what we're looking for. So, now that we have this set up here, our looks good, our Docker Compose looks good, we can actually get started running our indexer on our locally running chain to index all these events starting from block zero. Now before we actually run this, let me show you the deploy script for the NFT marketplace. So if we go to Foundry again, go to script, we go to setupanvil.s.soul, this has all the code

that was run in our initial anvil implementation. So we can see if we read here we deployed an NFT marketplace. We deployed a cake NFT, a mood NFT and we called mint and list cake. So what did this do? Well, it baked a cake and then it approved the cake from the marketplace and then it listed the item. So just because of this, we know that when we run our indexer, we should see at least one list item event be emitted. So let's keep going. We deployed a USDC contract as well. Here's where we deploy the marketplace. Here's where we get a cake. Here's where we get a mood. And then it looks like we do four more mint

and list function calls, meaning we should have at least five total tokens listed on our marketplace. We have just mint cake, which doesn't actually list anything. Then we have some mood NFT minting, and then we have some USDC minting. But essentially we should see is five items listed. Now to test our indexer is actually working after we see the indexer list these items, we should try to buy one and see if the indexer actually catches that buy request as well. So that's what we're going to be looking for here. So now that our indexer is set up, we can actually try to have the indexer listen to our blockchain. So of course let's make sure

to be in our home directory here. Let's do pmppm anvil to start up our anvil chain with all of that code already built. Let's create a new terminal and we'll run pmppm rundev. This will obviously spin up our website. And then another new terminal where we can run our indexer start and then it'll tell us we can do the indexer, graphql, all or help. We're going to get to GraphQL, but for now we're just going to run the indexer. So in order for us to run this indexer, we first need to CD to the marketplace indexer folder because this is the one that has the rindexer.yml and the docker compose. So now we can run

rindexer start indexer. And I'm going to select don't allow for this. And the first time you run this, you're probably going to get this error connected to database, error connecting to server. Uh, this is because it's starting up your Docker Compose and it doesn't have that Docker image installed yet. So, you're probably going to get this error the first time that you run this. And that's okay. Yeah, the full error that you might get might look like this. Docker start up the container. Failed. No such file or directory. Hey, failed blah blah blah blah blah. Um, this is because it as of today, this is a bug.

Uh, I'm sure they'll fix this fix this at some point. If you're watching this and this just magically works for you, uh, great. But as of recording, what this is doing is setting up a Docker container. And even if it errors here, it should have done that. And we can just run it again and it should actually work now. And whoa, it did work now. Well, there you go. So, let's read the logs here and let's see what it tells us. So, starting indexer no code. This is all to set up the Postgress database. It registered the marketplace. It registered the events and then it started reading from the blockchain starting from block zero. So for NFT marketplace, it says no events found

between the blocks, you know, 0 and 66. This number might this number will probably be different when you run it. This is correct. Uh it says no events found between blocks 9 and 66 for item listed. Hm, that's interesting. But it says four events found between blocks 0 and 66 for network anvil. So it found four different item listeds for here. Hm. I thought we said five. Maybe it is four. Maybe I counted wrong. It found no events for item bought, no events for item canceled, four events for item listed. So that that actually might be correct. Hold on. Now I'm Oh yeah, sorry. It is just it is just four. 1 2 3 4. Yep. My bad. Anyways, um anyways, okay, cool. So, for those of you who are

not database experts, to see these, you can go to this newly created folder called generated CSV NFT marketplace. In here, it has um some CSVs. In here, it has a list of CSVs or some basically some data files which keep track of all these NFTs listed. The item bought one is empty. It just has these folder headers. The item canceled is empty. It just has these folder, excuse me, column headers, not folder headers. And for item listed, it has 1 2 3 four events it tracked. So, it has the contract address, the seller, the NFT address, the block number, the price, and a whole

bunch of other information associated with that event. This is fantastic. It's keep track it's keeping track of all of our events being emitted making it much easier to query. Now all of these events are being put into a Postgress database as well. Now the Postgress database is actually running inside of a docker volume. So if we do docker ps we can see all the docker containers running. Right now we have the marketplace indexer postgress one running. And if I do docker volume ls, we can see the docker volume here. So this is where the database is running and this is also keeping track of all of the information

that our indexer is keeping track of. So hypothetically because this is running right now, if we were to buy an NFT, it should track that. It should calculate it here and then it should add that to our Postgress database and it should add it to our CSV here. If you're familiar with Postgress and with Docker and you want to pause the video now and actually try to do a SQL query in your Postgress database, highly recommend that you do seeing is believing. But for those of you who are a little bit less familiar with Docker and Postgress, seeing it in the CSV is pretty much the same thing.

So what we can do now now that everything's running right I have my website running I have my blockchain node running I have my website running and I have myixer running is I can go to my locally running website and we can try to list an NFT or even buy an NFT and see if our indexer catches it. So let's just try to list an NFT because that button actually works on the UI right now. So, I am on I'm connected to my account 8. This is going to Yeah. So, what I can do first, let's actually bake an a new NFT. Let's go ahead to the cake NFT bakery. Let's bake us a new cake.

And it looks like we get cake NFT baked successfully. Token ID 5. We can even double check what it looks like down here. A disgusting looking green cake. But now what we can do is we go to the homepage. We can select list our NFT. Go to our since folder. We'll get the cake NFT address here. That was token ID 5. Let's set it for $10. Preview the listing. We'll approve first. Okay, I'll confirm. Approval successful. Now, let's list it. Let's confirm your NFT has been successfully listed. Let's go back to our logs here.

Let's go to the R indexer. Oh, and we're getting this not in a safe reorg block range yet. 67 is greater than range 14. Our indexer and we're getting not in safe reorg block range yet. 67 is greater than range 41. This is because I've restarted my anvil server a few times, but I have not restarted the database. So the database actually keeps track of the most recent block that it listened to events for. So I So if I wanted to do this correctly, what I should have done was remove my Docker volume and then rerun the instance. But this might be something

that you run into. So I want to show you it here. Essentially, yeah, our indexer keeps track of the latest block that it listened to. And I ran my indexer for a little bit and it listened to 67 blocks. So, we have to mine at least 67 blocks and then we'll see what's up here. Okay, now it's in the correct range. So, um I'm actually just going to remint and list because restarted the indexer because I didn't refresh the indexing database, but whatever. It's fine. We'll just make a new cake. Token ID six.

Okay, cool. Let's list it. constants.ts. Cake NFT is here. Token ID is 6 10. Yes. Let's approve. Confirm. List for sale. Yes. Confirm. And it looks like it's been listed. Now, let's go back. And we do indeed see in our indexer logs here uh indexed one new event. And if we go to our marketplace indexer CSV, we have a new item listed event right here. 1 2 3 4 5 1 2 3 4 5 We have a new item listed event here. Five instead of four. This is fantastic. Now,

let me show you something. So, if I kill my indexer now, what you'll see in this Now, I'm going to kill the indexer because again, one of the issues that you're going to run into is that every time you restart your in, you know, any indexed events are still going to be in your database and are still going to be in the CSV file, but won't be on your chain. So if you want to be more thorough, every time you stop your chain, maybe you delete your generated CSV folder and then you can do a docker ps. Uh let's kill this. So docker kill this docker image. Now we do docker ps.

It's no longer running. Do docker volumes ls or it's going to be docker volume ls. Then we can do docker volume remove this. Uh it says it's in use. So we want to do a docker ps- a. And we can see that this is actually still running. And then we can do a docker stop. Paste that there. Okay. Now we can do the docker volume remove. Still can't remove it. Huh. Okay. So docker remove. Let's do this. Okay. Cool. Now docker volume ls. Now we can remove that volume and we remove the volume and that essentially deletes the Postgress database. Now when

we restart the indexer right if I do R indexer start indexer error connected to database and then we're going to rerun it and it's going to do the docker compose up with a brand new database here. So if you want to refresh your database you basically have to kill the docker volume, remove the docker image and start again. Okay, cool. So now we have a way to actually have a database that has these events indexed so that we can read them from a database. Now what we can finally do is we can open up our components and go to our recently listed component and update this to read from our database.

So in the docs for our indexer, if we scroll down, there's a section called accessing data. There's GraphQL and direct SQL. For us, we're going to go ahead and use GraphQL to actually read from our Postgress database. Now, when your indexer is running, if you do Rindexer start indexer, it'll start this indexing database. But if I control C cancel it, and I do Rindexer start all, it'll start both the indexing database and a GraphQL server. And it has a little playground that we could view here where we can actually use this playground to help create queries on our database. And then we can basically just

copy paste them into our code. So for example, let me delete everything in here. Let's go to root. Let's say okay, we want to make a query. Hit the add query. Let's say we want to get uh all item bots. Let's select all item bots. And see it's like populating this operation section over here. And let's say we want to get oh excuse me actually query we would scroll down to nodes we'd say like plus for nodes and we can select the different values of the item bots we do network NFT address etc we can query this we'll get nothing so let's actually reset this query let's go to query all item listed let's go do

nodes and let's do NFT address and price. Now we'll hit play and all the way to the right we'll get the output. These are all the item listed events that happened and you have the NFT address and the price. So we can use this playground to help build queries that we can run on our front end in order for us to pull this data. So let's go ahead. Let's actually update this recently listed component to do exactly that. And this is where once again AI is incredibly helpful. Here is how I prompted my AI to give me some code.

What's nice is in here there's a schema page. Now, and if you go to the SDL, this has all of the schema information that an AI would want to know for us to grab. So, I'm going to copy all of this in here. Go to DeepSeek or whatever AI you're working with, and I'll say here is my GraphQL schema. Three back ticks. Paste it in. Read back. And here is my recently listed component in Nex.js. And let's go ahead. Let's copy this in here.

I want to use at tanstack/react query. Uh we're using this because in our project we go to the package. JSON. This tanstack react query is already needed for working with WagMe. And this is a package that allows us to write queries with, you know, especially GraphQL. Really well. I want to use tansacre react query to pull a list of actively listed NFTts on the marketplace. You can deduce this by getting all the item listed events and removing the item bought and item canceled with the same token ID, contract address, and chain. Here is my NFT box component to show them. And this is just copy pasted that NFT box

component that comes with your starting code. Let's see what DeepC gives us. Hopefully, it gives us something that makes sense. As always when working with AI, when it gives us code, we want to go through every single line and make sure we understand every single part of this. Hopefully, it gives us something that makes sense. If it doesn't give us anything that makes sense, well, we can go build it from scratch ourselves as well. So, let's see what it gives us. Here's how to modify your recently listed NFTts component to fetch and display active listings using your GraphQL schema and React Query. We see it's going to ask us for a GraphQL endpoint. Here we have it gives us an interface for active listing. It'll be much easier to read if we copy paste for now. Let's go over to recently listed and just paste everything in here. Okay.

Okay. So, it's getting the format price here. It doesn't know that that's a default import. So, we can update this. It's using request from GraphQL request. I actually don't want to do that. I'm going to dump that. Instead of request, I want to use fetch, but sure, let's keep going. recently listed NFTTS equals use query active listing array. Yeah, your AIS might give you something great and you can roll with it or it might give you something terrible. Uh so we're going to build uh what I want to build here. So what we want to what I want to do is on my playground here is I want to have a single query that first is going to get all item listed. So it's going to be all item listed. I want it to be the

first 20 or so. And we're going to order by the block number descending. So, I'll just leave that like that for now. And for nodes, we're going to get the pretty much everything. We're going to get the seller NFT token address, NFT address, the token ID. Where's the token ID? Well, the price for sure, the token ID, contract address, transaction hash, uh maybe some other stuff. We're going to at least get all this, but we don't just want the item listed stuff. We also want the all items cancelled. And for this one, we just want the NFT address and

the token ID to make sure we're getting the right one. And we want the item bought with the token ID and the NFT address. So if I copy this uh operation or this query that it basically gave me, I can now make this const get recent NFTts equals. You can do this little back tick in here. Paste everything and hit save. That looks pretty good. Instead, uh we're going to say we're going to grab the first 20. Um and this is a variable for us to pass that we could do. And I don't want to do that.

We're just going to kind of hardcode the code this. We're going to say we're going to grab the first 20 because we don't want to get every single NFT to populate the page. But we're going to order by block number descending and TX index descending. So we're going to get, you know, let's get in the block number. Why not? Block number. Good. Item canceled. Good. Item bought. Good. So this is going to be the query that we're going to run. We can go back to the UI. We can even do a little print. Um, actually, let me copy this. Paste it into operation. Hit play. Oh, uh, oh, actually, instead of, sorry, instead of nodes here, we'll just say nodes. And

same thing with here, instead of doing it as a variable, say nodes. Now, I'll copy this whole thing. Paste it in here. Let's run it. What's it matter about now? Cannot find field nodes on type item cancelled. Item cancelled. Oh, excuse me. This is all item cancels. Excuse me. All items bought. All right, let's try one more time. There we go. And so this is approximately the output that we're going to get from running this query. Using this field here, we can obviously deduce which ones are active because if they're listed and then bought or canceled, we can take them off. Right now, bought or canceled returns nothing.

So all good here, but instead of having to pull from the blockchain, we just pull it from our database and filter down. Now we could go one step further and have our database maybe automatically update so that we have an active table where we can just call. But if you want to do that in Rust, you can. And there's a future feature of the R indexer which will have that. But for now, this is the API call that I want us to make. to call this GraphQL query going to make an async function fetch NFTts. So return a promise of a NFT query response. So we need to define like what we want this thing to respond with and it's going to be responding

with this data over here. So we can kind of create that as an interface interface NFT query response and in the data section it's going to have all item listed which is going to be nodes of NFT item list of that and we're going to have to make that as an interface to interface NFT item which is going to be R indexer string seller string NFD address string thank you chat GBT token ID contract address transaction hash hash block number. I think that's everything. So all item listed, comma, all item bought and all item cancelled cancelled item. All item bought all item

cancelled. And instead of returning an NFT item, actually these are just going to return interface something that looks like bot cancelled NFT address string and then token ID string, right? Because on our query, all item bot, we're just returning the token ID and the NFT address. All item listed is going to return all this stuff. All item canceled and all item bought, we're just going to return token ID and NFT address. So, we're going to have fetch NFT return basically an object like this. We're going to create a function that's going to do that for us. AI can probably get this one correct, but I'm actually just going to write it out myself. So, we're going to say const response equals await

fetch. And this is where we're going to put our our GraphQL endpoint. and what DeepSync gave us having it be like an environment variable. That's probably a good idea. For now, I'm going to kind of cheat and just do SL API GraphQL because when we start and this is defining an endpoint that we're going to use for our GraphQL server in our next.config.ts. I've kind of cheated. I've already set this up. I've said okay anytime you see a call to API/graphql send it to this location or use the GraphQL API URL. This way you could make this a production build if

you change this to an actual server. Then we'll define the parameters of this request. We'll say this is a post method. Thank you chat GBT content type application/json body equals JSON.stringify stringify query get recent NFTts just like that. That looks good. And then we'll just say return response.json like this. Okay. And then what I might want to do so I could do like a console.log fetch NFTts. And what I could do is I'll run my anvil instance, run my website instance, run the server here with the indexer up. And what I can do is I'll

inspect my site now. Look up console. I'll do a little refresh. And I should see a response here. Aha, perfect. Cannot access get recent NFTs before initialization. Oh, okay. Well, looks like I messed something up up here. Get recent NFTts is down here. Okay. Well, well, I guess that makes sense. So, let's delete this. Let's put this under here. Paste. Now, we'll refresh my UI. And we see promise pending. Okay. Return or console.log await fetchnfts. Now we'll refresh. And we see that in our little console we have the responses in our UI that we can use to populate this

recently listed NFTs. Okay, cool. So our query is working. Nice. Let's actually use it in our codebase below. for us to use it. We're going to stick this in a hook that we're going to use to filter stuff out so that we actually only get the actively listed ones. So, we're going to create function use recently listed NFTTS. We'll say const data is loading an error. Um, for this demo, I'm probably not going to do anything with these, but for a production application, you know, you want your site to say, "Hey, like we're loading the NFTs." when you're loading the NFTs but this will be use query this

will return the NFT query response key will be query key will be recent NFTts right because this is or whatever you want to use query function is going to be fetch NFTTS so this use query is a hook to basically make GraphQL queries for us and the response is going to be in this data object. We want to return a list as an object. So, we'll say const NFT data list equals we're going to do use memo uh because we want to cache this list of NFTs. We don't want our website to just constantly be making

this API calls. And that means we have to oh, we already have use memo. And then we'll first just say if no data return empty. Then we'll say const bought NFTTS equals a new set of strings and cancelled NFTs because we want to subtract these out of all the listed ones. And we'll say first we'll loop through this list of data. Get all the bought NFTts. So we'll do data and you could get kind of the So we'll do data data.all. Oh, looks like got my back a little bit here. Oh wow, it's really got my back here. data.data.all item botn nodes for each item bot NFTts add item

NFT dash item token. So basically we're adding all of the NFTs first to the bought NFTs and then we're going to do the same thing for the canceled NFTs. So I'm going to copy paste this and do canceled NFTs like this. And then we're going to say const Oh, excuse me. This should be all item canceled. Then to get the list of available ones, we're just going to see the ones that are not in these two. So we'll say const availlisted nodes do filter the item if not item address or not item token ID return false const key equals uh that

key looks pretty good and return notbot NFTTS has key and not canceled NFT. So, I know I kind of quickly did that and I used AI to help me out. Um, but basically, we have all of the bots, all of the canceled, all of the listed, and we're basically saying only return the ones that the bot and the canceled do not have, and that'll be the list of availables. So, then we could say const recent equals uh avails slice. Let's say 0 to 100. This will actually give us the top 100 NFTs.

So we'll say recent NFTts. And then we can return recent NFTs mapap the NFT to token ID NFT.ken ID to some new objects that are going to have a token ID a contract oops contract address which will be the NFT. NFT address the price which is going to be the NFT. And anytime data changes that we put that now anytime data changes we're going to update this use memo. So remember data is this query right? So anytime data changes we're

going to update the data list. This is a little dependency here. And we'll say return is loading error and NFT data list. So if this didn't make total sense to you, no sweat. Pop it into your AI. This is a way that we're going to be able to pull that list of recently listed NFTts and cache it so that we can show it on our front end. So finally down here, export default function recently listed NFTs. I can say const is loading error NFT data list equals use recently listed NFTs that hook that we just created and well we could add some like

loading and error things here but I'm going to skip it for now and what we can do now now that we have this NFT data list so I'm going to scroll down to where it kind of just has this image placeholder right which on the UI let me do a little refresh Oh, we have an error. NFT item. Looks like this is an issue. So, instead of where that placeholder image was, let's go ahead and delete that. And let's loop through that list of NFTTS that we got. So, we'll do little JavaScript here. Say NFT. We'll do NFT data list each NFT to an NFT box where the key oh looks like it's giving me most of the code

here. Key is going to be the NFT contract address NFT token ID. The token ID is NFT token ID contract address and price. And if we look at the NFT box, this is just a way for us to render different images of the NFT. All the code has already been written for us which is really nice. So let's save that and let's uh go to our our front end here. So our anvil node is running, our front end is running, our indexer is running. So a little refresh on the front end. Oh, yep. No, that is definitely an issue. Reading nodes data.data all item botnodes. Oh, is it not nodes? Node.

Node. Node. Node. nodes. Nodes Let's do a little console.log here for two data. Yeah, for this. See what we see. We get undefined. So, our query might not be working. A little console.log allbot data.data.allite item bot. And I see in the front end it's actually all item bots. I did a whole bunch of weird spelling. So data data all item bots. So since it's all item bots back in our here this should be all item bots like this. And let's see if cancel spell

right data all item canceled. Oh wow spelling is not my strong suit. All item canceleds. Oh god. Um but okay. So we fixed the spelling there. And then this means this is this. Okay cool. So now, okay, great. Whoa. Whoa. Is this Whoa, our front end's actually working now. Now that I tweaked those uh those two spelling mistakes. Wow, this is fantastic. And you might get an issue like this where one of these is actually kind of this blank NFT. Well, that's because our database tracked a fifth NFT listed. However, we reset our anvil chain. So, this is showing up obviously

as blank. So, if we go back to the cake NFT, bake a quick cake here. Confirm. Uh, and something else you run into that's kind of annoying. If you inspect, you get an error baking a cake. Go to the console, you get kind of this internal JSON RPC error. If you debug your Anvil uh chain, you'll find that the UI, the website is requesting a really bizarre block header. It's because we've been updating our anvil node so often that our MetaMask kind of falls behind. The remedy for this is to just restart your browser, which is kind of annoying, but such is life. So, I'm going to go ahead. I'm going to restart my browser. It' be great if MetaMask had

a nicer way to do this, but alas. So, now we can bake a cake. We'll confirm. It'll actually bake my cake. Token ID 5 is now minted. And if we go to the NFT marketplace, we do a refresh here. And we go to constants. Grab this cake NFT. Paste it in there. Token ID 5. Let's say price of $10. Let's approve. Great. We got to wait for it to be mined. And then let's list it. Great. While that's listing, I want to go back. Bake another cake. Error. Baking cake because we're getting the same silly RPC error. Yeah.

I wish I wish MetaMask had a better way to just uh you know automatically fix this but oh well. You just restart your browser. Bake another cake. Um let's list this one as well. List your NFT. This is NF token ID 6 price of 10. Approve. Yes. Wait for it to be mined list. Yes. And if we go back to our indexer, we will I should have restarted my database here because now this is all screwed up. So this is actually one of the annoying things. You need to reset your database, reset your anvil chain, reset your browser, reset your front end whenever you're wanting to test all these whenever you're wanting to test

all these because you can run into issues like what I'm currently running into. But in any case, after some time, you can see all the NFTts on your UI. So I actually am going to do exactly that. I'm going to kill my indexer. I'm going to do docker compose down to kill the container to kill both docker instances here. Do docker volume remove marketplace indexer postgress data. Remove that. Now I'm going to restart my indexer here. I have to run it twice. I'm going to pull back over, refresh my UI. Now we have a couple of NFTts for sale. I'm going to

switch to a different account here from account 8 to a different anvil. Update. I'm going to buy one of these tokens. Oh, and uh and a recently listed. We need to update this. We need all of these NFTs to have a little link here uh where the href equals the buy NFT page with the NFT.contract address slash NFT token ID and a back slash link down here. Oh, and that oh and fix the parentheses. Okay,

great. so that when we click on it, it'll actually go to the buy page. So, let's refresh now. Oh, running into an issue. Oh, we need the key prop for this as well. Key equals let's use the same key as down here. Nice. Okay. So, now when I click one of these, it'll bring me to the buy page. And now we see there's four NFTs here. I'm going to buy one. Let's approve payment. And now we'll go ahead and buy the actual NFT with this other address. And we can flip over to our indexer here where it should listen for this transaction. Oh,

it looks like it reverted for some reason. Sorry, I just realized that Anvil, my Anvil 5 doesn't have any tokens. So, let's switch to Anvil 10, which should have some tokens. Let's try this again. Let's have Anvil 10 actually buy this. We'll approve. And now we'll go ahead and buy. Says congratulations. We flip back to our indexer. We can see it did indeed catch the item bought event. So now if we've done everything right, we flip back to our homepage. We only see three NFTts. And we do indeed. Let's do it one more time. We'll approve payment. Yes. And as you can see, this is where writing some playwright tests.

What might actually be really helpful because it's going to be really annoying for you to keep testing this yourself. Let's buy again. It has been purchased. And now we only see two. So if you've made it this far, you have just successfully built an indexer for your full stack web 3 AI vibecoded application. This is fantastic because this will allow you to spin up full stack applications with complicated UIs because you've built a badass indexer which allows you to query events, get events very easily, stick it into a database, and then pull from your database. Incredibly easy. You should be incredibly proud of yourself. We have one more thing to do before we wrap up

this section. We're going to talk about the compliance engine and building APIs there. But we just learned a lot. So, let's do a quick refresher. Let's do a quick review of all the things we just learned before I send you on a quick break. So, we were running into a problem. We have this NFT marketplace codebase and it allows users to list NFTts for sale. But there's an issue. The state the getter functions only have a way to get listings based off of a mapping. So you can't loop through every single NFT on Earth with every single token ID and that's it. So there's no way for us to just call some function. However, there

are these events that the codebase emits. It emits an event when an item is listed, canceled, or bought. So for us to get the list of active items, we could say, okay, well, let's just get all the items listed and then subtract if they've been cancelled or bought already. Well, how do we do that? Well, we could write some code to do that ourselves or we could use a paid solution or we could use this fantastic tool called Rindexer which will do all of that for us. So, we created a new RN we created a marketplace indexer folder with an rindexer.yaml which we updated to say, hey, we want to listen to this contract

address on the Anvil chain. Here's the AI. Here are the items. Here are the events we want you to listen to. And we want you to stick these in a database. Once you stick these in a database, which we created with Docker, we are going to be able to pull from that database using a GraphQL query called where we get all the items listed. We'll get the first 20. We'll get the items canceled and items bought. and basically just subtract out the items canceled and items bought from that list of listed items. Then we'll use that to show that on the front end. And this way, every time somebody buys an NFT, our indexer will capture that event, update the

database, and we can remove it from the user interface. Now, there's a lot to play with here, and there's a lot to explore with indexers, especially with our indexer. I think that this is going to be one of the most popular, mostused indexing tools on the planet very soon. So, I highly recommend you get really good at this tool if you're looking to build more full stack applications. One other thing we learned was that again when working with our AIS, our AIS might not be aware of a lot of the tooling that's out there or how to use it. When we first prompted our AI to give us the code here, it had no idea about our indexer. It had no idea about paid solutions and its solution was 90% correct but it was that 10% wrong which

would have made it incredibly hard long and tedious for us to debug and work with it. So this is again where understanding every single line of code in your full stack application is incredibly important. We would have copy pasted that code tried to run with it. It wouldn't have worked. We would have gotten really mad. We would have said f this. This blockchain thing doesn't really work and we would have moved on with our life. So again, it's so important to understand every single line of code your AI gives to you. And it's important for you to validate it in the same way you would want to validate a junior engineer giving you some code. So with all that being said though, now is a great time to take a break. Go get some coffee, go for a walk, go get some ice cream, go to the gym, because we

have one more thing left to show you, and then I'm going to send you on your way to go to some hackathons and build some amazing applications. So, go get that coffee. Go get that ice cream. And I'll see you soon. All right. So, let's continue on. So, our app is looking pretty good now. It's actually able to update accordingly when people buy NFTs and cancel NFTs. And we have the ability to list NFTs as well as buy NFTs. There's only one thing left that we want to add. And this is something that you don't have to add, but this can make your applications or services easier to use if you're under some type of regulation or you want to comply with some governmental body. The

beauty of web 3 is that smart contracts will always be censorship resistant because when they are deployed with censorship resistance in mind. However, applications and full however websites are often run on servers and if they're run on servers that means there's a centralized entity and if there's a centralized entity there is somebody who can be held accountable. So if you have servers and you're facilitating hackers doing illegal things, that's quite bad and governmental bodies will come down on you like a ton of bricks. So what we can do is we can add some code to our application to check for compliance to make sure no hackers are interacting with our website. Once again, we will never be able to stop them from

interacting with the smart contracts directly, but at least we can keep them off of our websites. And to do this, we're going to be working with the Circle Developer Console. This is the circle team, the team that creates the USDC token. And they have a lot of different tools that developers can use to make both their full stack applications and smart contracts better in different ways. And one such tool that they have is this thing called the compliance engine. What this tool essentially does is it makes an API call to the circle servers and database which are constantly updated with different addresses from hackers. And if a hacker is interacting with our website, we can go ahead and block them. So, that's what we're going to be adding to our application here. Now, to get started, we're going to have to go ahead and sign

up for the Circle Console if you wouldn't mind. Um, to sign up, you can come back to the GitHub repo associated with this course. Select the Circle signup button, and this will bring you to the Circle Console for the signing in, and we're just going to flip over to the sign up here. And we're going to go ahead and sign up. So, take some time to sign up. Once we've gotten signed in, we can come to the Circle Console dashboard. Now, the first thing that you're going to want to do is you're going to want to create an API key. This is going to be your authentication to make different API calls. And you're going to want to save this and you're going to want to take this API key, come back to your application, go to

your.local, and save this as an API key for your application. Now, we don't want to expose this API key on the front end. So what we're actually going to do is we're going to make an API that's going to run on our server so that when the user tries to interact with our application, it'll call our server which will have the API key stored in the server and not exposed to the user and we will check the compliance engine. Now once you have this key, we can go back to the dashboard. We can go to compliance engine and go to the API docs, which this is not the API docs.

So, let's go down to the check out our quick start guide. That's not it either. Uh, okay. We're going to go to the AP here. Here they are. The API docs. I've put a link to the API docs. A link to the I've added a link to the circle API docs to the GitHub repo associated with the full stack curriculum so that it's easier to get to. So to see how the compliance engine works, we can scroll down on the left side to the compliance engine and we can go to the circle API docs here. We'll scroll down to compliance engine. And this will give us a little rundown of how this actually works. So if you go to settings and we zoom out a little bit here, the way that this works is anytime you call the circle API with an address, it'll first

check some default restrictive rules. It'll check the circles sanctions block list, your block list, and frozen addresses. It'll check your allow list, which might override the circles list. Some configurable rules based off of risk of maybe they're maybe they've been identified as being on the sanctions list or terrorists or other governmental risk levels. And then some alert only rules as well. But essentially in order for us to test this, we should add an address to the block list so that we can see what happens when we get block and see what happens when we get an accept.

So for me, I'm going to pick my anvil 5 address, but you can pick whatever address you want. I'm going to add it to the block list. Block reason. I'm going to say unsupported, and I'm just going to say testing in here. And we're going to go ahead and block this. Now whenever we call this API and we say hey is this address a hacker the API the circle compliance engine is going to say yep that's a hacker you cannot allow them to interact with your website. So now we have an address here let's go ahead and actually test it out. So if we go to the API reference which again there's a link to this in the repo. We can go change from node down to curl. There should be a curl in here. Let's look for curl curl

curl right here. And let's copy this to our codebase. I'm going to make a new file just called curl or curl.sh. Paste this in here. And let's walk through what this is doing. So curl is a command that you can run on your CLI. Any Linux or Mac OS usually comes with this installed. If you don't have it, it should be relatively easy to install. But basically, we're just going to make an API HTTP post request to this endpoint with this header. Um, and this is the endpoint to check if addresses are acceptable or not. And this is the payload or the data that we're going to pass for us. We want to test this out by using this address that we just added to

the block list. So, I'm going to update the address to here. The chain, I'm going to say ETH Seapoleia. I believe that is what is supported. Let's just double check. Yep, Etholeia. These are all the supported chains here. I'm going to say ETH Seapoleia and the indem the idotency key. This always has to be unique. So, we can do a Uyu ID V4 generator. Just look up a ID generator site. Great. We can copy this or you can make one yourself. This is just a unique key giving to every address. This is a unique key given to every call. Every time you make a call

with a key, no matter what you update the address or the chain or anything else to, it will always return the exact same result based off the key. Now, what the docs don't show us is we also have to add our API key in here. So, the only thing that we're missing here is our API key, which for us is going to be d- header station bearer and our API key here. I'm just going to copy mine from here. Paste that there. Bearer like this. And now I should be able to Oh, well, let's let's actually kill all of these. Delete. Delete like this. should be able to paste this and see what

happens when we run this request. Enter. Oh, I missed a slash here. Oh, I missed uh this and a backslash. Okay, let's try this. Paste it in. Son of a And I need a quote here. Okay, let's try this. So, we run this and we get this response. Data result denied. Decision rule name your block list. So, this address got denied because it's part of our block list. So, that's what we want. Let's try a different address. That's probably not on the block list. Let's do anvil build 10. We'll paste that in here. We'll get a new UU ID. Refresh the page here. Copy this. Paste it into

here. Copy this whole thing. Clear. Paste. And we see data results approved. So, so if an address is on the block list, it's on a sanctions list, it's been identified as a group of hackers, Circle will say, "Hey, you probably shouldn't do business with that address." Otherwise, it'll say, "Yep, address is good deal." And go ahead. So, we're going to add this functionality to our app. So, it blocks malicious addresses that it shouldn't work with. Now, like I said, we actually don't want our API key hanging around on the front end. we'd much rather the code for making this call be on our server.

So with Nex.js, you can actually make server side running code by just creating a new folder called API inside of our app. Inside of here, I'm going to make another folder called compliance. Although we probably don't need to make another folder because this is going to be our only API. And we're going to do route.ts. This setup like this tells Nex.js JS that okay this is going to be an API running on the server. You shouldn't expose this code to the front end. Now the beauty of this is that the code for this is so simple that most AIs will do a good job at generating the code for this. So I'm going to do a prompt for DeepSeek and if DeepS gives me a good answer, we'll roll with it.

But if not, we'll build it ourselves. And again, if your AI gives you a crappy response, you can always come back to the codebase associated with this section. That's section three. Go to here. Be sure to just flip over to the main branch in the SRC app API/compliance and check out the route in here. So, I'm going to say here is a sample request to check the compliance of an address. And for the docs, what I'm going to do is I'm just going to flip this over uh to JavaScript. Where is JavaScript? I like fetch. So, we're going to use fetch. I'm going to copy this, paste it into DeepS here. I would like to add this to my Nex.js project as

an API in my src/appi folder. Here is an example curl that works. The reason I'm adding in this example curl is because we really just want to tell our model that we have an API key. Although I'm going to not give the actual key to the AI model, but I am going to copy this paste this in here and I'm going to say it needs a unique UU ID v4 for each independency key. I don't know if I'm pronouncing that right. an address will be a parameter but the chain hardcoded. We could 100% create like a mapping of chain ID to you know the circle

compliance accepted chain name here but we're just going to use etholeia for everything for the time being. So I'm going to hit enter and see what gives me. Like I said if it gives us something good we'll roll with it. If not we'll code it from scratch. All right so let's see what it's giving us. First create the API route file. Um we're not going to do that. Uh, we're not going to go over the top. We're just going to use API/compliance route like here. That's fine. We do need to install UU ID because this is going to be the tool that helps automatically generate random UU ID. So, I'll do pmppm add UU ID.

Since we're using TypeScript, we did not tell DeepSc this. I probably should have. We'll do pmppm add at typesu ID as well. Okay, cool. Now, what's next? create an environment variable circle API keys your API key here we already have that however something else that I'm going to make is going to be enable compliance check and we're going to set this to true so we'll first check this and then we'll check this this can be so that when we're running our app we don't always check for this which might be uh beneficial for debugging and testing and such okay great now implement the API route and it gave me all of this here.

I'm just going to give it a quick update. I'm going to say use Typescript instead of JS. It's usually pretty good at writing it in TypeScript. Let's see what it gives us for this just so that I can copy and paste the code a little easier. All right, so rewrote it in All right, so it kind of rewrote it in Typescript here. Let's first copy the JavaScript example. This looks save. Oh, cool. It gave us a whole TypeScript version. Let's go ahead and copy paste this. Let's see how this looks. Once again, you always want to be reading through whatever it gives you. Okay, so it's doing this next response and next request stuff. I guess that's fine.

Let's check out the export async function post because this is really what we're looking for. So, we're going to be given a request as an input. I guess it wants to do a next request. Uh, I actually just want to do a regular request. I don't want to do a next request and I don't want to do a next response. So, we're going to get rid of that. Instead of return next response, we're just going to return response.json. We're going to make this a little simpler. And we're just going to do error and address requires success going to be false like this. Or it's going to be response.json like this. Okay, cool. The new key UID4. Great. The chain is going

to be ETH seapoleia instead. Circle API key is process.v.circle API key. Let's check our EMV.local. That is indeed what our key is. Okay, great. If not circle API key, then let's return another error. Uh change the formatting of this here. And then we'll do success false like this. Okay. Uh let's also check we'll do const compliance enabled equals process v dot enable compliance check equals equals equals true and we'll say if not compliance enabled then we'll go ahead

we'll do a little console log compliance check is disabled return response JSON say success will be true is approved will be true and then we'll say data result approved and message check is disabled. This way we have a similar format to what our actual API call uh returns. So um so this will make it easier for us to debug and work with our code. Then finally down here we have our API key set up. We'll now do circle response equals await fetch. We could

probably hardcode this as a constant variable, but this is fine. That looks good. That looks good. That looks good. Body is JSON.stringify. Good. Good. Good. Uh, it's chain. Oh, chain's not good, actually. So, it's got to be chain. Chain like this. Oh, so that's probably good. Never mind. That is good. Chain chain. Okay, cool. Await Circle response to JSON. Circle response. Looks like we have this interface up here. I'm just going to get rid of this interface.

I'm going to get rid of these. Uh I think this is kind of unnecessary typing. And we can get rid of that. That means Okay, cool. And we don't need to type this. That's fine. Uh and this is where it kind of isn't what I want. Uh I want to do this instead. So I'm going to say uh is approved equals data question mark.res results equals approved because remember when we did our curl responded with this whole thing is going to oh it's cuz I let's add the key back in.

Let's run this again. So the whole thing is going to be data. So data result. So data. Equals approved. And we're going to say return response.json. We'll say success will be true. Say is approved is approved. And we'll say data catch error console.log error. And we'll just do response.json get rid of this internal server error. We'll say status 500 success false. Okay, cool. So now we have an API endpoint. It'll be whatever server we're on in our compliance

endpoint. And we can call this to check to see if we're interacting with a bad hacker. So, we can put this on the recently listed site. I'm going to go ahead. I'm going to put this exclusively on the buy NFTt page.tsx. We could put this anywhere, but for me, for testing, I just want to put it on this buy side. So now that we have this endpoint, we can add the compliance check to our buy side here. And to do this, we can basically create a use effect where anytime the address changes, we can check the compliance. So underneath our old use effect, we'll create a new use

effect like this. And we'll say our dependency will be the address that is connected. I believe that. Yep. Address is going to be use account. So this is the dependency. Anytime this changes, we're going to run some code. Oh, since we want to make this a an async function, we should do async function check compliance like this. We'll do a check compliance like this. We'll remove the async function here. We'll just say if address call check compliance. Great.

And our check compliance will now say if not address we'll just return. And then it looks like chatbt is giving us actually a pretty good idea here. We'll do const response equals await slappi slash compliance like this will be method will be post this us calling our own server headers will be yep chatbt you got it content type application/json and the body will be jsonstringify the address because in our API call in our request. We're just putting the address in here. So, we're

going to send our address and we'll say const result equals await response.json. And this is where the result is going to say whether or not it is actually approved. If it is approved, we should say great, you can use our site. And if not approved, we should say uh screw off. You're not allowed to work with this site. And actually, you know what? Thinking about this, this might be even better to do someplace else. So, I'm actually am going to copy this whole thing and we're going to go to the layout instead. I'm going to add it here. This way, no matter what page they're on, we're going to do a little compliance check here. So, we're going to import import use effect from React.

We're going to do const address equals use account. So, we're going to add that in here, which means we need to import use account from Wagme. Okay, great. Then we're going to do a use state const is compliant set is compliant to use state. It'll default to true. This means we need to import use state as well from react. And we're going to say okay result. And we want to check the result here. And we'll just say set is compliant result success and result dot is approved. And that's it. And then what we can do is if it is compliant,

we'll do a little code here. We'll say if we'll say is compliant and if yes, we'll do props. And if no, do a little div tag here that says you are denied. And I did something silly here. I don't need the scriggies here. Okay, cool. So now we have a way for our whole app to kick us out. Let's go run this and let's see if it's working. pmpp run dev and we'll do pmppm anvil. I don't need to run the indexer cuz we're not really testing that right now. But let's go to our site and let's see what's up. So if we're

connected Oh, I did an issue. Oh uh I can't do that here actually. Oh. Uh, whoops. Makes sense that I can't do that here. My mistake. Uh, let's just, uh, copy all this wonderful code and put it onto the recently listed, uh, no, not the recently listed page.tsx. So, let's just add this to the homepage instead cuz, yep, I'm going to undo everything. Sorry about that cuz I forgot you can't do it here. So, we're going to add it to the homepage, which means we have to import all these from React. Um, we have use account already. So now we just need to update the is compliant bit here. So if we're not connected, we're going to

do all that. I'm going to add a whole lot more. Um, so if we are connected, we're going to do this, but we're going to say uh is compliant. If yes, we're going to do all of this. And if no, we're going to do a little div. You are denied. Like that. And once again, I don't need these lines here. Oops. Uh, that looks better. Okay. So, one more time, let's try our new compliance engine. So, let's do a little refresh.

We don't see any NFTs here because I'm not running the indexer. We're currently connected to one of the addresses. That's good. Let's connect now with an address that I have denied. Let's hit update. Says you are denied. Wow. So with that, we've now successfully enabled our app to be compliant and actually block potentially malicious addresses. With that, we are completed with the final section of the full stack curriculum. So let's do a quick refresher of what we learned and then we can send you on your way. So in this final section, we created a piece of API

that runs on our own server to check the compliance of our application. We worked with the Circle developer console and the Circle API. To get this set up, we had to go to the Circle Console, create a new API key, and then we worked with the Circle Compliance Engine to actually make the API calls. We created our own block list where we added an address just to test out the functionality. But we know that the circle team also keeps up to date, a list of sanctioned addresses and addresses that are not good to interact with. And with that, you're actually done with the full stack web 3 AI coding curriculum of cipher updraft. The final challenge that I'll

have you do for this course is going to be to deploy this site to Fleek and add your own features to this application. Now, I really want you to take some time and do these because this site is different. This is not a static site. We have a server for our compliance API calls. We have an indexer. We're working with an indexer. We have the GraphQL server as well. This is not a static site. We are actually making API calls to our servers and services. So the deploying process is going to be a little bit different here, but I want you to find time to actually do this yourself. So this is the challenge for you. If you run into issues, be sure to

jump into the GitHub discussions. Obviously, be sure to use your AI companions as well. But with that, big congratulations. Now, one of the most important things that I want to tell you all here is now that you've completed this curriculum, you should 100% be going to a hackathon after this. This will help create a time boxed. This will help create a set of time for you where you have to create a project in a short amount of time, which will force you to iterate and develop some really cool things. But with that being said, big congratulations on finishing the full stack curriculum. Now is a great time to go get some coffee, go to the gym, go get some ice cream because you've done incredibly well to get this

far. Wow, we went through a lot in this crash course. You have come so far and you've learned so much. I know that going through these sections, you've learned a lot about AI, full stack, blockchain JavaScript TypeScript websites, the blockchain, and you are prepped and you are armed to go out there and build some amazing applications. Building seamless websites and apps for normal people to interact with is crucial for us to get this blockchain experiment correct. I'm very technical. I love smart contracts. You can show me a raw piece of hex data for a transaction and I can tell you what it

does. Most people can't do that. Most people won't be able to take the time to learn how to do that and we shouldn't expect them to. It is our job to make these tools, to make these websites, to make these apps easy to interact with, easy to use, and easy to stay secure. Now that you've gone through this course, I'm very excited for you to build some applications that are easy to use and easy to stay secure for people interacting with blockchain. So, with this being said, this is the last time I'll say it for this course. Congratulations. Now is a great time to celebrate. Get some ice cream, get some coffee, go for a walk, go to the gym, because I know you've learned so much.

So, once again, huge congratulations. I hope you've learned a lot and I'll see you next time.