If you're interested in learning
about blockchain, this is the course for you. Patrick Collins
is a veteran software engineer and longtime finance industry
developer. Be sure to leave a comment with something you
learned from this course. Welcome to the ultimate solidity
smart contract and blockchain Developer course, JavaScript
addition, we recently did a version of this video in Python
that has over 2 million views right now making it the most
watched smart contract tutorial on the planet. We'
ve learned a
ton from doing that first course. And if you love the
Python language, definitely be sure to check that one out.
We've taken all the learnings we've got from making that first
course, wrapped it up and put it into this JavaScript video for
you. If you're looking to get started in web three blockchain
smart contracts or any of these terms, this is the course for
you. And it's for anybody and everybody, no matter your
experience level in programming or in blockchain. Ideally, you
know
, a little bit of JavaScript before starting this. But if you
don't, no worries, we will help you along the way. And in case
you do want to learn a little bit of JavaScript before
starting here, there are some fantastic courses on Free Code
Camp to learn more, but you absolutely don't have to. And
really, any experience with any object oriented programming
language will work, right. So if you're brand new to coding, or
if you're brand new to blockchain, this is exactly
where you should be. And i
f you already know a lot about
blockchain or coding, this course will give you even more
deep fundamentals on the space. So welcome to the edge of the
rabbit hole. So for those of you who don't know, my name is
Patrick Collins. I'm a smart contract engineer, developer
advocate on the chain link protocol. And I live in breed
smart contracts. I also make YouTube videos on my own
channel, medium Twitter threads and more. teaching people about
smart contracts about coding and about this new technolo
gy. I
love helping developers learn, grow and learn about this new
Advent. That's blockchain and smart contracts. And I'll be
taking you on your journey to becoming a blockchain expert and
Wizard of the smart contract developing world. And even if
you don't want to become a developer, the first section our
blockchain basics, will give you a lot of fundamental knowledge
about how blockchain and smart contracts, even work. I am so
excited for you to be here. And I hope you enjoy the journey.
This
is a data dump, passion educational project of
everything I've learned working in this space. And I am 100%
certain that if you follow along, you'll come out the other
side of this, armed with the knowledge to be a positive force
in this incredible industry, solidity and smart contract
developers are massively in demand with an average salary of
being around $145,000 a year, there is massive economic
opportunity from learning this course. And this technology has
the ability to revolutionize ever
ything that we interact
with, and you can be one of the pioneers ushering in this new
age. In our courses. We already have a track record of giving
people these educational needs and sending them into their
careers in the blockchain and smart contract space. And we are
going to give you all of the cutting edge tools and how to
use them including working with things like D phi NF TS Dows,
ERC 20s upgradable, smart contracts and more. We're going
to teach you the skills necessary to build defy
app
lications like Ave synthetics and compound, billion dollar
decentralized applications, or massively successful NFT
projects like crypto punks, or board apes. Dow is like maker
Dow or developer, Dow and any of the amazing things you've seen
in the cryptocurrency world. In learning these skills, you will
have all these economic opportunities at your fingertips
and the ability to be a pioneer in completely changing the way
we interact with each other. In a fundamental way. Building
decentralized sm
art contract applications is building a world
that's more accountable a world with unbreakable promises, a
faster, more efficient, financially free world, a
collaborative community, combining the prowess of
philosophy and technology into a new system. We'll learn more
about the purpose and the value of smart contracts and
decentralized applications in lesson one of this course, and
why they're so exciting. Finish this course. And you'll be
ready. So again, I want to give a major thank you and a
major
kudos to you because you've taken the first step to enter
this realm. So welcome to Web three. Welcome to the
blockchain. Welcome to smart contracts. And I'll see you at
the bottom of the rabbit hole. So let's jump into some best
practices for this course so that you can learn most
effectively and learn with this course as best you can. You do not want to skip this part. It'll help you solve
80% of your issue. Now, while going through this course be
100% certain to follow along with a GitH
ub repository
associated with this course we have a link in the description
of this video for you to absolutely click on follow along
have open in a tab as you're doing this because it has all
the code Sam polls, timestamps a community to interact with and
more, it is going to be your Bible for watching this course.
And yes, we have a Discussions tab. This Discussions tab is a
place where you can ask questions, engage with other
developers who are going through this course as well. Get some
help
, and etc. Be sure to say hello and meet other like minded
individuals like yourselves. Now, blockchain and smart
contracts move really quickly. And things are constantly being
updated. So to make sure you're always up to date with the
latest, when I open up some documentation, try to open up
the documentation for yourself as well. And maybe even have the
code sample next to you. And as you're writing code, be sure to
refer back to make sure that you are keeping pace, and then you
have the most
up to date samples, sometimes technology
might change, and there might be a way to do something a little
bit better. So we have this file called chronological updates, be
sure that this is the first place to check when you run into
an issue to see if maybe something was updated that you
missed, it'll be chronologically ordered. So it's easier to find
updates. Basically, this is to say, always refer back to the
GitHub repo first. And if you do to find a mistake, or something
isn't working the way
you expected, jump into the
conversation, leave a discussion, leave an issue, ask
questions here, take breaks, I cannot tell you how many people
tried rushing through our entire first course in one setting and
didn't retain as much information, your brain
remembers information much better if you take breaks. So
every 25 minutes or half an hour, maybe take a five minute
walk. And then every two hours take a much longer break. And if
you really want to make sure something drills in, try to go
bac
k and reflect on what you did for the lesson prior before
continuing to the next lesson. And at the same time, though,
learn at your own speed. There is no right speed for this
course. If it takes you a day, a week, a month or even a year, it
doesn't matter. Learn at the pace that's right for you, you
can change the speed at which I talked using the little gear
icon in the YouTube video. If I'm talking way too fast for
you, you can slow me down. And at the same time, if I'm talking
too slow, you
can speed me up, you also don't even have to go
in order. You can bounce around topic to topic, if you don't
want to learn full stack, you can skip the full stack stuff.
If you don't want to learn coding, we can skip the coding
stuff. If you only want to go to the advanced stuff, go to the
advanced stuff. You are highly encouraged to pause, come back
and ask questions. The blockchain and smart contract
world is incredibly collaborative. So be sure to use
tools like the discussions tab of our Gi
tHub repository, asking
questions on Stack Overflow and Aetherium Stack Exchange and
tagging relative technologies, making issues on different
GitHub repositories that you're working with jumping into
discord, Reddit, Twitter, and any other place that these
communities and technologies are congregating. And the reason I'm
putting so much emphasis on these community aspects is that
becoming a solidity and blockchain engineer is so much
more than just the solidity part. Being comfortable with all
the tools in this space, including the ones to get help
and to give help are essential to being successful here.
Networking is massive, and it makes it a ton of fun. As you
continue your journey and you get more advanced and you're
looking for places to meet other developers. Hackathons are one
of the best places to connect with other engineers, the
chainlink hackathons eath, global hackathons, and Dev folio
hackathons are three great hackathon suites to connect. And
no matter where you are in y
our journey, they're great places to
flex what you've learned. Alright, so those are some of
the best practices for this course. You're standing at the
edge of the rabbit hole, looking down, peering into the world of
web three and smart contracts and blockchain. If you're like
me want to jump in, you want to keep going further. Let's begin
our journey into the world of smart contracts. And it all
starts with the blockchain basics. Now, I know you're excited to get coding. But before we
jump in,
we want to learn some of the fundamentals of
blockchains and smart contracts. Understanding these ideologies
and these basics are so important, because it'll dictate
how you architect your decentralized applications,
learning the basics of blockchain and solidity is
critical. But if you already know the basics of a blockchain,
feel free to jump into lesson two. Now, since you're here, you've probably heard of Bitcoin
before. Bitcoin was one of the first protocols to use this
revolutionary techno
logy called blockchain. The Bitcoin white
paper was created by the pseudo anonymous Satoshi Nakamoto, and
outlined how Bitcoin can make peer to peer transactions. In a
decentralized network. This network was powered by
cryptography, decent Tality, and allowed people to engage in
censorship resistant finance, in a decentralized manner due to
its features, which we'll talk about in a little bit. People
took to this as a superior digital store of value, a better
store of value over something like g
old, for example, and
that's why you'll also hear people commonly refer to it as a
digital gold similar to gold. There's a scarce amount or a set
amount of bitcoin available on the planet, only so much that
you can buy and sell. You can read more about the original
vision in the white A paper we've linked to the white paper
in the GitHub repo associated with this course. Now, this was
an insane breakthrough in a little bit, we're going to learn
exactly how this is all possible and how this actua
lly works
under the hood. Some people though, saw this technology and
wanted to take it a little bit farther, and do even more with
this blockchain technology. And a few years later, a man named
Vitalik Buterin, released a white paper for a new protocol
named Aetherium, which use this same blockchain infrastructure
with an additional feature. And in 2015, him and a number of
other co founders released the project Aetherium, where people
could not only make decentralized transactions, but
decentr
alized agreements, decentralized organizations, and
all these other ways to interact with each other without a
centralized intermediary or centralized governing force.
Basically, their idea was to take this thing that made
Bitcoin so great, and add decentralized agreements to it,
or smart contracts. And in fact, technically, these smart
contracts weren't even really a new idea. Back in 1994, a man
named Nick Zabo, had actually originally come up with the idea
smart contracts are a set of instruc
tions executed in a
decentralized autonomous way, without the need for a third
party or centralized body to run them. And they come to life on
these blockchains, or these smart contract platforms, like
Aetherium. And it's the smart contracts that are going to be
the core thing that we're going to be working on in this course,
and that we're going to be developing, you can think of
smart contracts in the same way you think of traditional
contracts or traditional agreements. They're just a set
of
instructions between parties, except instead of written on pen
and paper or typed up in Microsoft Word, they are written
in code and embodied on these decentralized blockchain
platforms. And that's also where they're executed. Instead of
being executed by the two parties, or three parties, or
however many parties that are involved. This removes this
centralized issue that we'll talk about more in a bit. This
is one of the main differentiators between the
Etherium protocol and the Bitcoin protoco
l. It's these
smart contracts. Now, technically, bitcoin does have
smart contracts, but they're intentionally Turing incomplete,
which means they don't have all the functionality that a
programming language would give them. This was an intentional
move by Bitcoin developers. Bitcoin developers viewed
Bitcoin as a store of value versus Aetherium, developers
viewed Aetherium as both a store of value and a utility to
facilitate these decentralized agreements. Now, the smart
contracts on blockchains
alone are absolutely incredible.
However, they do come with a huge issue. If we want these
digital agreements to replace the agreements in our everyday
lives, they probably are going to need data from the real world
blockchains by themselves actually can't interact with and
can't read or listen to data from the real world. This is
what's known as the Oracle problem. These blockchains are
deterministic systems and they're deterministic on
purpose. And we'll learn about more about how that works
in the
sessions to come. So everything that happens with them happens
in their little world. But if they're going to be these
agreements, they need external data and they need external
computation. And this is where Oracle's come into play.
Oracle's are any device that delivers data to these
decentralized blockchain or runs external computation. However,
if we want our applications to stay truly decentralized, we
can't work with a single Oracle or a single data provider or a
single source that's
running these external computations. So
we need a decentralized Oracle network. Similar to our
decentralized blockchain network, you're on chain logic
will be decentralized, but you also need your off chain data
and computation be decentralized, combining this on
chain decentralized logic. With this off chain, decentralized
data and decentralized computation gives rise to
something called hybrid smart contracts. And most of the
biggest protocols that we interact with today are some
type of hybr
id smart contract or interact with hybrid smart
contracts. To some extent, this is where the protocol chaining
comes into play. It is a modular, decentralized Oracle
network that can both bring external data and external
computation into our smart contracts to make sure they're
decentralized end to end, while giving them the feature richness
that we need for our agreements. chainlink allows for us to get
data to upkeeps, get random numbers or really customize our
smart contracts in any meaningfu
l way. Now, throughout
the course, we're going to use the terminology smart contract.
However, whenever we say smart contract, we're often using it a
little interchangeably with hybrid smart contracts, but just
know that when we say hybrid smart contract, we're talking
specifically about smart contracts that have some type of
off chain component. Now since the theorems release, a number
of different block chains or smart contract platforms have
come to light such as avalanche polygon, Phantom ha
rmony, and
more. For the majority of this course, we're going to be
assuming that we're going to be deploying to the Ethereum
network. However, everything that we learned here is going to
be applicable to the vast majority of the blockchains out
there like polygon, avalanche, Phantom harmony, etc, and
understanding everything from a theory and funding mentals will
give you the skills that you need to switch chains very
easily with literally one line of code. So don't worry about
learning a speci
fic tool or with a specific chain because most of
them work together seamlessly. Now, there are a couple of smart
contract platforms that don't use solidity. But still learning
the fundamentals here will make you much better at those as
well. And the Etherium by far has the most value locked and is
the most used blockchain and smart contract platform out
there. You'll also hear those two terms used a little bit
interchangeably as well. Sometimes they'll say smart
contract platform, sometimes the
y'll say blockchain, they
kind of mean the same thing. Obviously, blockchains could
mean store of value and smart contract platform, but you get
the idea. Similarly, chainlink is the most popular and powerful
decentralized Oracle network is the one that we're going to be
focusing on for this course as well. chainlink is also
blockchain agnostic. So to work on Aetherium, avalanche, Polygon
Solana Terra, or really any other blockchain out there. Now,
throughout this course, you'll hear the term DA
P or
decentralized Protocol, or smart contract Protocol, or
decentralized application. And they all kind of mean the same
thing. A decentralized application is usually the
combination of many smart contracts. And when we get into
solidity, you'll see what a singular smart contract really
looks like. And like I said, learning all these core
fundamentals will make you a better solidity and a better
smart contract developer, you'll also hear the term web three a
lot in this video. And in the indust
ry, web three is the idea
that blockchain and smart contracts are the next iteration
of the web web one being this permissionless open source world
with static content, web two being the permissioned web with
dynamic content, but all the agreements and logic runs off of
centralized servers where they control your information. And
then web three comes back to the permissionless web. But once
again, with dynamic content, and instead of centralized servers,
running your logic, decentralized network
s, run the
logic creating these censorship resistant agreements, that the
smart contracts enable is also generally accompanied by the
idea that the users own the protocols that they work with,
and it's an ownership economy. You'll see what I mean later in
this course. And we've talked a lot about the history and about
the high level of these protocols and of the smart
contracts and what they can do. But what do these smart
contracts really mean? What is it when I say trust, minimize
agreements o
r unbreakable promises? What is the real value
add of these smart contracts? Before we look under the hood,
take a peek at how this all works. From a technical
standpoint. Let's learn what all the value of this is. What is
the purpose of us building all these technologies? Have you
taken this course? What problem does this technology solve? In
my mind, a technology is really only as good as the problem that
it solves. If it doesn't solve a problem, then why bother Mark
contracts, Blockchain web
three cryptocurrencies, those are all
just different words that encapsulate the idea of what
we're doing in such a unique paradigm. I think the easiest
way to sum up what these smart contracts do is that they create
trust minimized agreements. And if you might be scratching your
head to that. A much easier way to think about it is just that
give rise to unbreakable promises. Yes, you heard that
right. Unbreakable agreements and promises. Additionally, they
give rise to speed, efficiency and tran
sparency, and a number
of other things. I made a video pretty recently about exactly
this. So let's dive in and take a listen to the purpose the
undeniable value of smart contracts
cryptocurrencies, fundamentally re landscape markets and
agreements as we know them. Unfortunately, you've probably
only been bombarded with people screaming about empties and
money. Now, some of the memes are fun, but let's forget the
bullet and get down to the essence of the space. If you're
already in web three. Th
is is the video to send to your
friends to explain why you're so excited about this space and
explain why we're here. And then if you're not into crypto,
you've come to the right place. And yes, there are fun memes and
markets and there's some money stuff in there all these things.
But outside of all that the purpose of blockchains relates
to the age old elementary school unbreakable, promise the pinky
swear, let's get Froggy. Nearly everything you do in life is the
result of an agreement or a c
ontract. Your chair was the
result of an agreement to buy and sell lumber to assemble and
sell the chair to a real tailor on Amazon. Then you made an
agreement to buy the chair for $40. The lights in your house
are powered by electricity, which is an agreement from you
and the electric company you agree to pay them in return
they'll keep the lights on the electricity they generate it's
agreements between them and engineers who built turbines to
generate the electricity with insurance you agree t
o pay some
amount of money to them every month, and in return, they will
do nothing or I mean, they'll cover your medical bills, almost
everything you do and everything you interact with is the result
of some form of agreement or contract in some aspect. Now,
agreements and contracts can feel kind of abstract and boring
to really grasp on to simplify, we can also refer to them as
promises. When you get an oil change. They're promising that
they will faithfully change your oil In exchange for mon
ey, when
you put money in the bank, they promise to keep it safe in
exchange for them to use your money to give out loans. When
you buy a lottery ticket, the lottery promises to give you a
fair chance at winning a ton of money in exchange for you buying
the ticket. Whenever you make one of these agreements. In a
way, you're asking them to pinky swear to not screw you over and
to treat you fairly. But this doesn't always happen. Let's
look at a real world example of someone breaking the Pinkie
sw
ear back in the 80s and 90s, McDonald's ran a promotion for
people to win money by collecting McDonald's Monopoly
game cards. The idea was simple. You buy McDonald's in return,
you get a chance to win $1 million. You can imagine
McDonald's literally going Hey, everybody, I promise you if you
buy our MC food and McNuggets, we'll give you a fair chance of
winning this money, we ended up breaking this promise, instead
of having a fair chance of winning your chance was in fact,
zero in the mid 90s.
Between 13 and $24 million went into the
pockets of not people playing the game, honestly. But a group
of corrupt insiders who had rigged the game, meaning that
when you played the McDonald's Monopoly game, you were buying
into a set of lies and promises that were 100% always going to
be broken. And the thing is, it doesn't really matter if this
was McDonald's fault or not. They were the ones making the
promises that they ultimately could not keep. Another way you
could think about it is that th
at's $24 million that they
essentially stole from you and I now if this system was deployed
on a blockchain was something called a smart contract, it
would have been impossible to defraud this $24 million due to
smart contracts being immutable, decentralized and transparent.
But I'll get back to that in a minute. In all the agreements
and contracts we make, imagine making a pinky swear with a 10
year old and imagining how that agreement would hold up. Hey,
buddy, Could you could you please keep
my money safe? You
can play with it if you like, but just please have it when I
come back. Immediately. You might get that worrying feeling
in your chest, something might go wrong. This 10 year old might
lose your money. You might be thinking, how could I trust
them? Will they break their promise and this feeling of I
can't breathe because of untrustworthy situations
happens? Once all the time. Can I trust this used car
salesperson to give me a good car? Can I trust this tag that
says machine wa
shable? Or will it make my shirt shrink? Will my
insurance provider break their promise of covering my medical
bills want to get hit by a bus? I'm Patrick promises he'll go on
a hike with me. Well, he actually I actually will. But
the issue with our current agreements and contracts is we
have to trust the people. We're making them to do the right
thing. However often they're actually incentivized to not do
the right thing. insurance doesn't want to pay out money.
Sometimes salespeople just want
to get the shit off the shelves
and with my girlfriend, I promised to go on a hike. But I
need hikes. Where else is has happened. Now you might be
thinking okay, Patrick, this seems cool. But like Where has
this actually affected me? Well, the McDonald's lottery that we
just spoke about above during the Great Depression with the
run of the banks banks promised to keep our money safe and that
when we went back to go get it they would actually have the
money there. And well and behold there were t
imes that they
didn't have the money they're just last year Robin Hood
painted this amazing picture. On user application we will give
you access to the markets we promise we will give you a
retail investor a fair chance of interacting with the world the
finance side, but not this asset. This
asset, this asset or this asset, the 2008 financial crisis
remember that shady deals behind closed doors combined with lies
about financial product brought the world to its economic knees,
how are you? hyper
inflation in Zimbabwe hyperinflation in
Brazil, fair enough. US history is a relentless lesson of
trustworthy entities being notorious promised breakers and
we finally have a way to fix it with smart contracts. Now,
before I jump into smart contracts, a lot of people might
be thinking, hey, cool in all, however, we have systems in
place to protect against a lot of these things, which is true,
and which is great. And that is a very helpful step forward. But
these systems often breaks ones in 2008
Definitely didn't work.
The ones with the Robin Hood crisis definitely didn't work.
And even if these systems apply, and you go to court to try to
work them out, maybe you're in court for years before you
actually see a resolution and by that time, what you needed the
money for is longer. So what is this technology? What is this
tool that can fix this fundamental problem in our
agreements today, this tool is smart contracts and this tool is
what the blockchain was built for. Now, I'm gonna give
you a
quick overview of what a smart contract is. However, I'm
leaving some links in the description for more in depth
explanations, but the basics of them is a smart contract is an
agreement contract or a set of instructions deployed on a
decentralized blockchain. And once the contract or set of
instructions is deployed, it cannot be altered, it
automatically executes and everyone can see the terms of
the agreement. Real basics of it is that the code is executed by
decentralized collective lik
e a group of people but a group of
people running a certain software. This means that no one
person or entity can actually alter any of these agreements or
change the terms of the arrangement in these traditional
agreements. Whoever owns the contract, whoever owns the
execution of the contract can flip a switch and say, We're not
going to do that anymore. In smart contracts and web three in
blockchain, you no longer can do that. Typically, these smart
contracts are on a decentralized blockchain,
and used in
combination with a decentralized Oracle network to get the real
world assets and information. And if these words sound like
I'm conjuring up a magic spell, well, again, check the links in
the description. If you want to learn more about the technical
implications. If you're not a technical person, then you're
not interested in getting into the nitty gritty, you can kind
of think of it like HTTPS, I bet the vast majority of you don't
even know what HTTPS stands for. And yet you use i
t every single
day whenever you log on to the internet. So how does this fix
the McDonald's Monopoly issue? In its traditional form, the
lottery was executed behind closed doors, somebody operated
and owned the code and the contracts and the agreements
that ran the lottery, and they had the power to alter it. And
nobody other than the people internal on the lottery could
audit this altering happening. Now if the code for this lottery
was deployed onto a blockchain, every time a hacker attempted
to
alter it, everyone would be notified. Not only that, but you
couldn't even alter it, because the terms of a smart contract
cannot be altered. Once deployed, combine that smart
contract with a chain link VRF Oracle to get a verifiably
random number. And presto, you now have a perfectly
decentralized, unalterable agreement that is impossible to
hack commit fraud or manipulate we have just saved the public
between 13,000,020 $4 million just by fixing the issue of
trust. How does this fix Robin H
ood? Well, the problem with
Robin Hood is already fixed. Right? Again, the problem is
that there's a centralized body that can flip a switch at any
time and say you can't access these markets anymore. We're
breaking our promise of actually giving you access to the
markets. This is already fixed with something called
decentralized exchanges. And these exist today, one of these
exchanges is one called uniswap. You can swap ERC 20 tokens,
which are kind of the equivalent of stocks, but some are som
e
are, it's a little confusing. I won't get into that here either.
But it doesn't have that centralized body that can flip a
switch and ruin access to the markets. And had these investors
been on a decentralized exchange, it would have saved
them hundreds of millions of dollars, and it would have
prevented fraudulent market manipulation. How does it fix
run of the banks with transparency built in an
automated solvency checks, you can build a bank like smart
contract that has insolvency checks bu
ilt in that make it
impossible to get there and solve it means brokers any
agreement or any history lesson, where there was a trust
assumption that was broken, smart contracts can be applied
to and should be applied to, especially in a time where big
money runs, owns and controls everything, we desperately need
to move to a world where some self interested centralized
entity can't flip a switch and ruin people's access to the
services that they need. We can move away from a world that is
brand b
ased to a world that is math based. Right now, if you
interact with a service that you don't like, or that they break
their promise, the only thing you can do is walk down the
street to the next service that's going to make the same
set of promises. And you have to hope and pray that they're
actually going to keep it we can move from that to a world where
we can just look at the map and say, oh, okay, one plus one
equals two. This is what this agreement is going to do for me
every single time gu
aranteed, because it's a decentralized
autonomous agent has no incentive to be evil, and
everything is transparent and out in the open of a big
company. And if it was better for me for one plus one to equal
three, maybe I would go behind some closed doors and flood some
numbers and come back and be like, hey, one plus one equals
three with smart contracts. That's impossible. Doing the
right thing is infrastructural now, given the choice between
two agreements, one where you have to trust a singl
e
centralized entity that they're going to do the right thing for
you, versus a decentralized untampered double collective, which one you're gonna choose.
I'm picking the one that can't screw me over every single time.
For every agreement I can apply it to now this technology is
relatively new, but we have already seen it relandscaping
entire markets and continue to do so the traditional financial
world is already getting its lunch eaten by defy or
decentralized finance. There's already over $20
0 billion of
people's money in these protocols to help have a more
fair, more accountable, more transparent financial system.
This defy movement is one of the main reasons I got into this
space because we desperately need to move away from where we
are right now. And and people's chances for wealth being sucked
up by some group that's bending the rules in their favor and
smart contracts are our ticket to that better world. More and
more industries are also coming over to smart contracts and
bloc
kchain because of all the innovations and because of all
the advantages that it has as we grow and as we get better as we
get closer to this vision of having this concept fulfilled,
trust minimized agreements. These smart contracts are
minimizing the trust that we need to give other people in
order for these agreements to be executed. If trust minimized
agreements is too confusing for you just say unbreakable
promises. Now I gotta be honest with you guys. blockchains and
smart contracts and cryp
tocurrencies can actually do
more than just trust, minimize agreements. They have security
benefits, uptime benefits, execution, speed benefits, and a
whole lot more. But it's a lot easier to just learn about one
and learn the other ones later, right. It's kind of like
sprinkles on top. So this is why we are here. This is why we're
building this future and this is why we are so excited about it. Even in just this introduction
part, we've learned a ton. So let's do a quick summary of what
we've l
earned so far. Bitcoin was the first protocol to take
this blockchain technology into the limelight and take these
cryptocurrencies into the mainstream Bitcoin is a sort of
digital gold or a store of value able to make transactions
between users in a decentralized manner. Aetherium and other
smart contract platforms take this blockchain technology one
step further, enabling people to make smart contracts and
decentralized trust minimized agreements, the smart contracts
and decentralized applicat
ions can access and interact with the
real world using something called decentralized Oracle
networks. chain link is a decentralized network that
allows us to build these hybrid smart contracts, which combines
our on chain logic with our off chain, decentralized data, and
decentralized computation, giving rise to our logic being
completely decentralized, and our data and external
computation being completely decentralized, giving us all the
features that traditional agreements and traditional
co
ntracts have. Now these digital currencies like
Aetherium, and Bitcoin have value. Even without the smart
contract part, having a censorship resistant,
decentralized store of value is naturally powerful in its own
right, we have some links in the GitHub repository that will
teach you how this decentralized store of value flips traditional
finance on its head. And it's another one of the great reasons
for building smart contracts. But again, the easiest way to
boil it down is trust minimized agre
ements, or unbreakable
promises. But let's also go into some of these other features
that smart contracts have over our traditional environment. The
first feature, of course, is that they are decentralized. And
they have no centralized intermediary, the different
individuals that run one of these blockchains are known as
node operators. And it's the combination of all these 1000s
of node operators running the same software running these
algorithms running these smart contracts that make the netw
ork
decentralized. We'll dive deeper into how that works later. The
next feature is transparency and flexibility in these
decentralized networks. Since all these individual node
operators run the software, everybody can see everything
that's happening on chain, meaning there's no shady deals,
there's no weird things happening. Anything that's going
to be unfair, people will be able to see and just not use,
everybody has perfect information and has to play by
the same rules. Now additionally, thi
s doesn't mean
that there's no privacy, the blockchain is pseudo anonymous,
meaning that you aren't necessarily tied to an identity
in real life, they also have the feature of speed and efficiency.
For those of you who have ever tried to do a bank transfer or
send money across seas, you know, it sometimes can take two
to three weeks, when in fact, all these banks are really doing
is basic math. They're subtracting money from your
balance and adding it to some other balance. Why does it take
so l
ong, in the blockchain, all of these transactions happen
instantly. Another instance for those in the financial world
today know that clearing houses and settlement days can take a
long time. In the blockchain, there's no need for any of that,
because they happen instantly. This obviously is much quicker,
but it also makes for much more efficient interactions with each
other security and immutability. Again, immutable means that it
can't be changed. Once a smart contract is deployed, that's it,
whatever is in the code is going to be in the code forever, they
cannot be altered or tampered with in any way. This means that
the security is much easier. Whereas in a centralized world,
somebody can hack into the server, jump into the database
and change some numbers. You can't do that in the blockchain
world. And since it's decentralized, in order to hack
the blockchain, you'd have to take over half of the nodes as
opposed to in the centralized world, where you only have to
take over one in
the regular world. If your computer and your
backup computer go down, all of your data is gone. In the
blockchain world, if your computer and your backup
computer go down, all your data is safe, because it's being run
on all these other decentralized nodes. And even if a few 100
nodes or a few 1000 nodes go down. It doesn't matter because
as long as one node has a copy of the blockchain, you're good
to go hacking a blockchain is nearly impossible, and leaps and
bounds more difficult than hacking
a centralized server.
Not only that, but this is safer in the asset sense as well. All
you need to access your credentials and your information
in your assets is your private key. Which is basically your
password for all of this. And as we've discussed in the video,
the smart contracts remove this counterparty risk, remove this
centralized intermediary, remove these trust gateways that we
have to do in web two. When we engage with users and
individuals, they don't always have our best interests
at heart
smart contracts, remove this counterparty risk, because once
one of these contracts is created, they can't go in and
they can't alter it. And they can't let greed or ego or
anything else, get the better of them and alter the terms of the
deal. And as we said, this gives rise to these trust minimized
agreements or these programmatic unbreakable promises. We move
away from brand based agreements to math based agreements, where
we can look at the cryptography we can look right at the code
and see exactly what something is going to do and how it's
going to execute, versus having to rely on a human being doing
the right thing with smart contracts and decentralized
hybrid smart contracts. Doing the right thing is
infrastructural all these pieces boil down to us having the
freedom to interact the way we want to interact without having
to be afraid that interacting like that is going to screw us
over this trust minimized piece, these unbreakable promises, make
interactions so much be
tter. In a purely web two world, we're
constantly bombarded with messages of projects and
protocols pushing us to move or act in the direction that makes
them more profitable. Versus in the smart contract space, we can see everything
transparently. And we can even engage in interact and be
partially owners of the protocols and the interactions
that we decide that we want to be a part of. So smart contracts have been around for a few years now.
And what did they generate for what industries have
come about
due to these smart contract platforms being around? Well,
you've probably heard of some of these and some of these we've
already mentioned, but let's give you a quick refresher, d
phi d phi stands for decentralized finance. And it
gives users the ability to engage with finance and markets
without having to go through a centralized intermediary. For
example, like we said, with Robin Hood, you no longer have
to trust that Robin Hood would continue to give you access to
the markets, you
instead would be able to see in the smart
contract. Yes, I have access to the markets or in the 2008
financial crisis. You never have to trust that these groups and
institutions are giving you the correct things on the back end.
You can see everything transparently right on the
blockchain, you can engage with things like money markets and
sophisticated financial products easy effectively and securely at
the time of recording. defi has around $200 billion in assets
under management and is quickly
growing. If you're really
excited about defy we have a ton of defy examples showing you how
to build and interact with these protocols. In coming lessons.
Dows or decentralized. autonomous organizations are
another group that we've already mentioned. Dows are groups that
are governed completely decentralized by a set of
instructions or smart contracts on chain. There are some massive
benefits here where engagement is much easier. The rules are
black and white. And you can see everything directl
y on chain
voting and governance technologies completely
decentralized in the blockchain space is one of the big ones
pushing how we can evolve politics and how we can evolve
governance to make it more efficient, fair and reasonable.
And you better know it, we have some examples of how to build
Dows and how to work with Dows incoming lessons. So be sure to
watch those NF T's Stanford non fungible tokens and can really
be kind of described as digital art or just a unique asset, they
can do so muc
h more, but we'll keep it high level for now.
Projects like board apes and crypto punks have revolutionized
the way that people get paid for their work, show off their
creativity, status, and so much more. And yes, of course, we
have lessons showing you how to create and interact with NF T's
as well, so many other groups and so many other industries are
being created as a result of this insane technology. And
maybe after finishing the journey with us here, you go out
and you'd be the one to pion
eer the next industry or the next
billion dollar idea you've learned so much already. But now
that we've learned a lot of this high level information, let's
finally jump in, and let's make your first transaction and let's
get you set up to interact with this new world. In this next
section, we're going to get you a wallet and we're going to show
you exactly what a transaction looks like and feels like. Let's
dive in. This is the Aetherium website aetherium.org We are
going to make a transaction
on a test Aetherium blockchain I'll
explain what that means in a little bit. This is going to be
our first transaction that's made on the blockchain. Now
again, this process that we're going to follow is going to work
the exact same with polygon, avalanche Phantom and all these
other EVM compatible blockchains. I'll explain what
that means in a bit too. For now, just follow along and have
fun in order to make a transaction on any of these
blockchains. The first thing that we need to do is we nee
d to
set up a wallet. So I'm gonna go ahead and go to meta mask
because it's one of the most popular wallets and one of the
easiest to Use, we're going to go ahead and download it. I'm
using the brave browser, but it works for Chrome, Firefox or
really any other browsers. And it's just going to be a little
extension in the top right hand of your browser. This way, we
can really easily see it any times what we have in our
wallet, will still are all of our Aetherium based currencies.
So I'm gonna
go ahead and install meta mask for brave, add to brave, add extension. And
now we can go ahead and get started with working with Brave,
this is the first step you absolutely need to take when
starting your journey and one of the easiest steps to take. So
we're gonna go ahead and get started. And we're going to
create a brand new wallet. So we're gonna go ahead and hit
create wallet. If you already have a wallet, you can actually
import it via I have a seed phrase, and we'll talk about the
seed p
hrase or secret phrase in a little bit. So let's go ahead
and create a new wallet. And sure, we'll agree to help out
Metamask now we will create our password make sure that this is
really secure. For the purpose of this demo, my passwords are
just gonna be password. But please don't have that be your
password. You may also get a video like this teaching you
about your secret recovery phrase. This is the same thing
as your pneumonic. But see your recovery phrase is a lot more
clear as to what it
is. And again, to give us a ton of
different tips on how to actually store it and keep it
safe. The main takeaway from this is never shared this
absolutely never shared this. So we're going to go ahead and
click reveal secret words. I'm showing you guys here because
this is just a demo and I don't really care. However, if you
show this secret phrase to anybody else, they will have
access to all the funds in your application. So everything that
we're going to do in this tutorial, we're going to u
se
fake money, we're going to use not real money, so it doesn't
matter. Now for the purposes of testing and developing, I always
recommend using a completely separate meta mask, a completely
separate Wallet. So for going throughout this entire course,
if you already have a wallet, or if you already have a meta mask,
please just set up a new one, create a new profile, create a
new meta mask, and this will be your wallet that you use for the
duration of this course. However, if you're going to
act
ually put money in here, you absolutely need to have this
written down. Because if you lose access to this, and all
your private keys, which we'll talk about in a little bit, you
will lose access to your wallet, and you will lose access to all
your funds. So they give some tips like store this phrase and
a password manager like one password, write this phrase down
on a piece of paper, put it in a secure location, memorize it,
whatever you want to do, just make sure you have this backed
up somewh
ere, I'm just gonna go ahead and hit download this for
now, it's not best practice to save to your computer, it is
much better to use a password manager or write it down on a
piece of paper or something. So we're gonna go ahead and hit
next. And it's going to ask us to verify that we actually have
it written down. And we're gonna go ahead and hit confirm, and
great and gives us a couple other tips. Remember, definitely
take these tips very seriously, especially if you're gonna use
this for real
money. Like I said, For this demo, we're just
going to use test money. So it's not as big of a deal. But if you
put real money in, you absolutely need to backup this
seed phrase or secret phrase or we're going to refer to it as
our pneumonic phrase. Awesome. Now we can see the interface of
our wallet here full screen. And depending on your browser, you
can actually come up and pin it to your browser so that you can
just click it up in the top right, and it'll drop down and
you can see the same i
nterface here our pneumonic phrase that
secret phrase, those that secret 12 words that they gave us have
given us access to a new account, the address of our
account is located right here. In fact, if we click it and copy
it to our clipboard, and go to a tool called a block explorer
called ether scan, we can actually paste our address in
here and see details about our account. Ether scan, like I
said, is what's known as a block Explorer. And it's a way to view
different addresses transactions an
d other happenings that happen
with a blockchain. If we look at this address that we just
created on ether scan for Etherium main net, we can see no
transactions have happened. There's really no analytics,
there's no comments, there's no balance, there's no value,
because it's a brand new wallet, and this address that we just
punched into ether scan represents our unique address
our unique wallet only identifiable for us. We'll talk
about ether scan a little bit more in a bit because it's a
tool
that we're going to use quite often in wallets like meta
mask, you can actually even click right here and create even
more accounts. So let's go ahead and create a new account. We'll
call this account two. As you can see, this one has a
different address. So if we click this one, we go back to
ether scan. We paste the address in here. We hit Enter. We can
see another address again that's uniquely identifiable to us,
right here. It is zero balance, no value, no transactions now If
we go back to
our Metamask, and we click the little button, we
can see we have two different accounts in here, it's the same.
If we hit the extension in the top right, click the button, we
have two different accounts. The 12 word secret recovery phrase
allows us to create multiple accounts, all with the same
secret recovery phrase. So that secret recovery phrase will give
us access to both account one and account two and any other
accounts that we create by hitting this Create Account
button, because it gives
you access to all the accounts in
your meta mask. Now, these addresses of both of our
accounts are the public unique identifiers, but they also have
a private unique identifier only identifiable to us. Similar to
the pneumonic, these are private identifiers we never want to
share and we never want to give out their private This is known
as your accounts private key. So the mnemonic will give you
access over many of these accounts, the private key will
give you access to just one of these accoun
ts, we can see it by
hitting these little three dots, going to account details and
export private key, you'll just have to punch in your password
here. And you'll be able to see your private key. This is going
to be your private key for your account, you can think of your
private key as a password for your account that lets you
create transactions. Now the reason that I'm showing mine on
screen is because I'm not going to put any real money in here.
And this is just going to be a burner account
for this
tutorial. And I highly recommend once again, you use a burner
account, you use accounts that you never put any real money
into. And along the way, I'll show you how to make sure that
you don't do that. But normally, it's not a good idea to show or
share your private keys or your secret recovery phrase, if
somebody gets a hold of this private key, they will have
access to my account one. However, they won't have access
to my account to if they get a hold of my 12 word recovery
phrase or
pneumonic they'll have access to both accounts. And
this is why when people say keep your private keys safe, your
keys, your Bitcoin, your keys, your Aetherium, they're talking
about both your mnemonics or your secret recovery phrases.
And your private keys keep those private, your public addresses
are totally public. And anybody can view your accounts on
something like ether scan, or any other explore. And it's
totally okay for people to share their public addresses. If you
lose your private ke
y, you lose access to one of your accounts.
If you lose your mnemonic, you could potentially lose access to
all your accounts. Basically, what I'm trying to say is back
these up and keep them in safe places for this course it's okay
if you lose one, since we're not putting any real money in them.
But in the real world, be sure to do this. And great. Those are
some of the main security considerations here. Now, if you
look up in the top right, right next to that account button that
we've been cli
cking, you also see this thing saying Etherium
main net, this is our networks tab. And if we click it, we can
see a list of all the different networks that we currently have
access to a Etherium main net is the main network of Aetherium.
And this is where real money is spent and used for transactions.
For this course, we're not going to be working with the Etherium
main net, we're instead going to be working with something called
a test net. Since we're engineers, oftentimes, we're
going to want
to test and see what our code is actually going
to do and how to interact with it. We're going to use a
combination of local networks and test networks to actually do
this to actually test our smart contracts. We're mainly going to
use local networks. But we'll get to that in a little bit to
see some of the test networks that come default with meta
mask, we hit show slash hide test networks, this will bring
us into the settings page. And we just hit select this to show
test networks in the list
. And we just hit on Now if we scroll
back up, we'll close out of the settings, we hit the network's
tab again. Now we can see all of these other networks here like
ropsten, Koven, Rinkeby, and Garelli. These test networks or
networks that resemble Aetherium, or polygon, or
Avalanche or phantom or any of these other blockchains. And we
can actually switch our accounts to one of these other test
networks. Let's click rink B, for example, we can see that on
the Rinkeby test network. We also have z
ero Aetherium we have
no money or nothing in here, we have a blank Rinkeby wallet.
These test nets work nearly identical to how Aetherium main
networks except for they run with not real money. They run
with fake money as a way for us to learn and interact and see
how these different smart contracts actually work together
at the time of filming. rink B is one of the most popular test
networks along with COVID. So we're going to work a lot with
Rigby In this tutorial, however, be absolutely sure t
o check our
GitHub repository to make sure that you're always up to date
with the best test network for following along with the
tutorial here since their test networks people are running them
out of the goodness of their hearts. And sometimes the best
ones actually change so, so be sure to follow along with the
GitHub repository. We might also use COVID From time to time or
maybe even Grilli. So we're going to show you how to use a
couple of these different test nets. In fact, if we go to the
G
itHub repo associated with this course, we can see recommended
test net is indeed currently ranking. So that's what we're
going to work with should this change, you should be able to
follow along with another test. And we'll leave notes as to how
to continue. Now what we can do actually is we can go to Rigby
ether scan, we can go to, you can look up Rinku ether
scan, and it looks like it's the first thing that shows up
Rinkeby dot ether scan.io, we can punch in this same address,
copy and paste
it. And we can see some of the details of this
address on the Rinkeby ether scan. Like I said, right now,
it's totally blank. This networks interface later on is
also how we're going to be able to work with polygon, avalanche,
etc, we'll just have to add networks. But we'll get to that
in a bit. And just to reiterate, test nets are free and for
testing our smart contracts and main net networks cost money and
are considered live. Now I also do want to put a caveat here
that we do want to keep in
mind that these test nets are being
run at the goodness of people's hearts. So we don't want to
abuse them, we want to use them to learn and then move on. So
try not to send a billion transactions on one of these
test nets. In fact, what we're going to do right now is we're
going to send a transaction on the Rinkeby test net. And this
will show us exactly what it would look like on a main
network. In order for us to simulate one of these
transactions, we're going to go to what's called a faucet.
And
if you go to the GitHub repository associated with this
course, right underneath the recommended test net is going to
be a test net faucets, which is going to show us where the most
up to date faucet location is for us getting test net
Aetherium. So here we are at faucets dot chain dot link,
which again is the recommended faucet, and what we can do is we
can actually put our wallet address in and get some test net
link or test net Aetherium. Now what we are gonna have to do is
we are gonna
have to connect our wallet to the Rinkeby network.
So we're going to come down, we're going to switch from COVID
to Etherium. Rigby, and then we're going to make sure our
meta mask is on the Rinkeby test network here. Once both of those
are set up, we're gonna go ahead and hit Connect wallet. And
we're going to choose meta mask. Once we do that, our meta mask
is actually going to pop up and say would you like to connect to
this website, connecting to a website is how we give these
websites in in
terface to interact with our wallets and
interact with our meta masks. Don't worry, we're not sending
any transactions like this, we'll get to that in a bit. So
we just we can pick an account we want to connect. Let's choose
our account one, we'll hit Next. And then we'll go ahead and
connect. Now that we're connected, we can actually see
our account connected up here. And that little warning is now
gone. And our wallet address is automatically placed into here,
we're going to make our first tes
t transaction. And for now,
we don't need test link. So we're going to leave that off.
But later on, we're going to come back and get that test
link. For now we're just going to need 0.1 test Aetherium. So let's go ahead and complete
the security by choosing the traffic lights. And we're going
to hit Send Request. What this is going to do is we're asking
this faucet to send us 0.1 test Aetherium test net faucets are
ways for us to get money into our wallets on a test net. And
this is why this te
st and Aetherium isn't worth any actual
money. Since we can get it for free. These don't exist on Main
net, you can't get real Aetherium or real money for free
on a main network. So we're on Rinkeby we're getting fake
Rinckey Aetherium. And we're gonna go ahead and hit send
request. Once we hit Send Request, this transaction hash
is going to pop up here, and it says transactions have been
initiated waiting for confirmation. This means that
some other wallet is actually going to send us 0.1 test
eath.
And this is the transaction that it's doing to do that now we
just have to wait for our transaction to finish verifying
and finish going through. Now if this doesn't work right away, I
would recommend Wait a minute and then just try it again. But
what we can do is we can click this transaction hash. If that
transaction doesn't show up, we can also just close this. And we
can copy our address here. And actually we already see 0.1 eath
in our wallets here. But we can go back over to Rinkeby
ether
scan, paste our address and and we can see that we now have 0.1
ether as a balance. We can also see that we have a transaction
with all this information going into our wallet. That's what
this green is for. If you click that transaction link, you'd get
something like this, but If you didn't, don't worry, because on
the ether scan, if you click the transaction hash in the
transaction list, you can also see all the details like that.
So now in our meta mask, we have 0.1 eath. Again, this is
fake
Aetherium. And we have a transaction associated with our
wallet now, which is awesome. Again, though, if we switch
networks, if we switch networks back to Etherium main net, you
can see that we have nothing on a theory main net. Or if we go
to ropsten, we also have nothing, we only have this 0.1
eath. On the Rinkeby test network, if you want to practice
working with another test net, and the faucet that we're using,
has multiple test nets. Let's go ahead and try it during this
section right
now is completely optional, you can watch or you
can follow along. But for example, I can see in my wallet
that we already have COVID supported, so maybe I'll switch
to COVID. Maybe we'll switch to COVID. In the drop down here,
we'll remove test 10 Link, because we only need test eath.
We'll hit I'm not a robot, and we'll send request and the same
things will pop up this time, this is going to be for the
COVID test net. And once our transaction finishes going
through. Now, same thing on COVID.
Here, like what we did
with rink B, once our transaction finishes going
through, we'll see 0.1 test eath on the COVID network, if you
want to go ahead and try working with another one of the test
nets. Like maybe, for example COVID recommend you go ahead and
giving it a try. But it's completely optional. And I would
always refer back to the GitHub repo to make sure you're working
with the most up to date faucet and test net. And if we look
back at ether scan, we can actually see more details on
what actually just took place. What actually just happened, how
did our Metamask get a balance of 0.1 eath. All of a sudden?
Well, if we looked down in the transaction section, we can see
that there's a transaction here, some address sent us 0.1 ether.
And if we click the transaction hash, we can see more details
about what actually went down with this transaction. Now
understanding what's going on in this transaction is essential to
learning and being a smart contract developer or just
engaging
with the ecosystem. So let's learn the first bit at the
top is this transaction hash. This is a unique identifier for
this blockchain or this test net that identifies this exact
transaction. This transaction hash identifies sending 0.1 eath
to our address, we can see that the status of this transaction
was successful, it didn't break. In any case, we can see the
block number that this transaction was included in and
we'll get to blocks in a little bit, we can see the timestamp
which of course i
s when this transaction occurred, we can see
which account it was from which if we go ahead and open in a new
tab, we can see that this is the account that this transaction
came from. And it's got 3 million ether. Of course, this
is fake Rinckey ether. So it doesn't really matter, we can
all see who it was to, which again, is just us. This is our
wallet address 0x 106 X blah, blah, blah, cero x 1066, blah,
blah, blah, right, the value of this transaction, of course, is
0.1 ether. Now what's all
this that we see as the value so
obviously, the value is 0.1, because that's a mode which we
sent. But we see this transaction fee. In this gas
price, we hover over the tooltip, we can see if you zoom
in on your ether scan, you see amount paid to the miner for
processing the transaction. And we see a gas price which is cost
per unit of gas specified for the transaction and ether and
gray. The higher the gas price, the higher the chance of getting
included in the block. Now if we scroll down even
more, and we
click See more, we can also see a ton of other information here.
For now we're just going to click to see less and just focus
on these two. I'll explain all of these in a later session. Let's talk about just the concept of transaction fees
and gas for a second. Remember how I said the blockchain is run
by all these different nodes will all those different nodes
are running this blockchain because they actually get paid
for all the transactions that happen on these blockchains
whene
ver you make a transaction, there's a node or a miner or or
a validator somebody running the blockchain software is gonna get
paid a tiny bit of Aetherium or polygon or whatever blockchain
that you're running on, they're gonna get paid a tiny bit of
that native blockchain currency. This payment is obviously to
incentivize people to continue to run nodes and they calculate
how much you pay and how much the node operators get paid
based off of how much gas you use. So there's this concept of
gas.
Gas is a unit of computational measure. The more
computation a transaction uses, the more gas you'd have to pay
for. For example, we do hit click More just really quickly.
We can see this section say A gas limit and gas usage by
transaction, there was a limit of 60,000 units of gas on this
transaction, and 21,000 or actually use. So this
transaction use 21 units of gas. Now for very simple things like
sending ether, the units of gas are usually pretty cheap. But
maybe for more complex things lik
e like minting NFT,
depositing to some defy contract, etc, maybe those will
cost more gas because they'll be more computationally expensive.
And this is a little confusing right now, don't worry too much
about it. But just know that we use 21,000 gas here. And if we
pull out the calculator 21,000 gas times this gas price right
here, times the gas price, we get the exact same as we see for
the transaction fee. So gas price, times how much gas you
used, is the transaction fee. So whoever sent us t
his 0.1 ether,
also paid 0.0000525, etc Rinkeby ether to make this transaction.
Now, each blockchain has a different way of actually
calculating how this gas stuff works. So that's basically going
to be the high level of it. So we're going to focus just here
for now there's a total transaction fee. And then
there's obviously the gas price. After we cover how blockchain
works, I'll explain what this burn stuff is these gas fees and
all these other stuff. For now, just know that anytime you make
a
transaction on chain, you have to pay a little bit of what I
call transaction gas. So for example, if we go to our
Metamask, we have two accounts right here, we have account one
was 0.1, Rinckey eath, and account two was zero. Rigby. If
I were to send 0.05 Rinckey eath. From this account to my
other account, how much rinky eath Do you think I'd have left?
Well, let's go ahead and try it, this will be the first
transaction that you're actually creating that you are going to
spend the gas for. So
if we go ahead and hit send, we'll hit
transfer between my accounts. count two, we'll do 0.05. Next, we can see some information here about
what's actually going on Metamask has some new advanced
gas for UI and settings, we're also going to turn the song, so
go ahead and click that enable enhanced gas UI, turn that on,
and then go back. And again, this is going to be the
experimental tab. But it could also just be in the General
Settings tab. Depending on when you actually run this, we can
see
a little notification here. Again, this depends on what
version of Metamask we're using. And we get this little drop down
that says Here are some of the different type of gas fees that
you can actually pay. The reason that gas fees might change, as
you can see here is that depending on how busy the
blockchain is, you have to pay more gas. If a lot of people are
sending transactions, that means there's not going to be enough
space for everyone's transaction to get through. That's a bit of
an over
simplification of what's happening. But don't worry too
much about it for now. Now, if we want to send the 0.05 ether
to our second account, we can see this gas estimated section,
which is saying it's estimating, we're going to pay 0.00004792
gas in addition to sending the 0.5 eath. So at the bottom, we
have amount plus gas fee. And this is going to be the total
amount that we're going to be spending on this transaction
0.05 is what we're sending. And we also have this gas piece. So
we go ahead
and confirm, we now see we have a transaction
pending in our Rigby ether scan. And if we click on it, we can
even hit View on block Explorer. And a Rigby transaction hash
will pop up and depending on when you click it, it might say
indexing, this means that ether scan has received your
transaction and is trying to place it. If you don't see
anything here, it means that maybe the transaction hasn't
gone through yet. Maybe you need to wait a little bit more. Or
maybe you need to go back to the Git
Hub repo and pick the
recommended testament and faucet. So you might have to
wait a minute or so for this to actually finish indexing. After
a minute or so we can see that this transaction has indeed
passed. And we can see a lot of the same information that we saw
on our last one, this time with 0.05 ether. And now if we look
in our meta mask, we'll see we can see account one has 0.05
It's rounding up a little bit, we click on the big button, we
can see it actually has 0.049953, etc. And our oth
er
account account two does have exactly 0.05. This is because we
spent a little bit of Aetherium on gas to send this transaction.
And now with just this little bit of information, you know how
to actually interact with applications that use the
blockchain, how to send transactions and a lot of the
non technical details. Now here's something that's
incredibly exciting with just this little bit of information.
You now know how to interact with blockchains and interact
with the Etherion protocol.
So if you don't want to learn how
to code anything, you can go If you can start interacting with
Aetherium and interact with protocols with just as much
information. However, I know most of you guys are here to
learn how to code. So let's look under the hood of Aetherium. And
what is actually going on with these transactions, and what
these gas and what these blockchains. And what's really
going on, let's learn all the fundamentals of a blockchain.
Now, if you want to just go ahead and jump into
the coding,
go ahead and grab a timestamp from the description. However,
learning exactly how the blockchain works is going to
make you an incredibly powerful developer. So let's take a look at that
first. So we're going to be going through this blockchain
demo on this site right here. Now, the creator of the site has
a fantastic video and a fantastic walk through
blockchain one on one, it is right on their site. So if
you're looking for another explanation, definitely check
out his video, it i
s absolutely fantastic. But the first thing
that we really need to do in order to understand blockchain
or just on really anything, and everything that's going on here
working first really need to understand this Sha 256, hash,
or hashing just kind of in general, let's first understand
what a hash is. A hash is a unique fixed length string,
meant to identify any piece of data, they are created by
putting some piece of data into a hash function. In this
example, the hashing algorithm used is Sha
256. Now Etherium
actually uses this, this right here for its hashing algorithm,
which isn't quite Sha 256, but as in kind of this SHA family.
But it's really just another way to hash things. And the specific
hash algorithm doesn't matter so much. So this example, you just
shot up to six, but you can imagine it's the same as the
Etherium. Hash, they're just going to result in a different
hash. So what's going to happen in this application here is
whatever data or whatever information we put into
this
data section here, as you can see below this hash changes. So
what's happening is this data is running through the Sha 256 hash
algorithm. And it's outputting, this unique hash. So this hash
is a unique fixed length string, that's going to identify like a
blank data piece here, right. So if I put in, you know, my name
like Patrick Collins, this is the hash that's going to
represent Patrick Collins, right. And you can see, even
when I put, you know, tons and tons of data in here, the length
of the string doesn't change, right. So it's always gonna be
the same, we can put almost any amount of data in here, there is
an upper limit on the max size of the data. But for all intents
purposes, we can pretty much put any length in here. And you'll
see to that every time I type in Patrick Collins, this hash is
always gonna be this seven e five D, right? I'm gonna delete
I'm gonna do Patrick Collins, again, you're 75 B is always
this, this unique hash is always going to be unique, right, it
's
always gonna be this fixed length string here. So now we
can take this idea while putting this data in here, we can move
on to this concept of a block. So with this block concept,
we're going to take the exact same thing with this hash this
this data section, right, but instead of having everything
just being in this, this singular data area right here,
we're going to split this data up into block, nuns, and data.
So all so what we're going to do is we're actually going to hash
all three of t
hese to get to get this hash, right, we're gonna
put all three of these, we're gonna say all three of these are
combined. Together, we're gonna put every all three of them into
this hashing algorithm to figure it out. So if I type a bunch of
stuff here, we can see that block one with nonce, you know,
this nonce, and this data, we're going to get this hash. And as
you can see, actually, the screen turns red, this block
turned red. Now, what happens when I hit this mind button?
When I hit this min
d button, it's actually gonna take some
time, it's gonna think for a little bit. And we can see that
the nonce here actually changed, right? The nonce is different
from what it was before. And this hash now starts with four
zeros. Okay, and then the back turn green. When we're talking
about mining, we're talking about miners solving some type
of very difficult problem that takes a lot of time to do now in
this example, here, the problem that the miners had to solve was
they had to find a nonce,
or or a value in this nonce section
that when hashed with at block number one with this data, it
would start with four zeros. So the problem here the miners had
to solve was to start with four zeros and the only way for them
to really do that is kind of this brute force, you know,
trying stuff so they tried one okay, one didn't work. Okay,
two, nope, two didn't work. 3456 Okay, five, well, that started
with one zero, but it's not four. And they have to keep
trying all these numbers until they ge
t to this one where you
know, let's hit mine again. Where it has four zeros at the
top at the start. Now, this specific problem changes
blockchain to blockchain right yet. Aetherium has a different
problem for miners to solve A bitcoin is different problems
from yourself, but this concept is going to be the same. So they
have to take it, one block is going to be this, this, this
concept is going to be all this data, it's going to be the block
number. And it's going to be this nonce, right. And s
o this
nonce is the solution is going to be the the number that they
use to get like the solution to the problem, right? So if I go
to one here, you know, I do this again, hit mine. And the nonces
changed, right? And went from one to 33,128. Because this is
the nonce that allowed this hash to start with four zeros. And so
that's what's happening. When blockchain miners are mining
they're going through this process is very computationally
intensive process of trying to find a nonce that fulfills
whatever the problem is. So that's really it, actually. So
that's a block. And that's really what's happening when
miners are mining. They're just looking, there's trial and
error, brute force trying to find this nut so so now that we
know what a block is, let's go to the next step and figure out
okay, well, what's a block chain. So here we have an
example of what a blockchain is going to look like. Right, we
have a combination, you know, we have back here in the block
section, we have one what
one block looks like. Now here, we
have multiple different blocks, right, each one of these
represents a different block, but we have an additional column
here, we have additional variable here. So like before,
you know, we have block nonce and data, right, we have blocked
nonce data, we also have this thing called previous right, and
so this is actually gonna be pointing to the previous hash of
the last block. So for example, if we go to the last block in
this blockchain, it says previous 008.
And if we look at
the hash of block number four, is 00008. And then we look at
its previous it's four zeros, B nine, we have four zeros, B,
nine, and so on, all the way back to our first block, which
has previous of just all zeros, right. And so the block with the
previous of all zeros, is going to be known as the Genesis
block. So you've probably heard that before the Genesis block,
it's the first block in the blockchain were the previous
hash points to a hash that doesn't actually exist. Now,
as
you can imagine, kind of the same as how this block worked,
how the block nuts and dated all go through the hashing algorithm
in the blockchain, the block nonce data, and previous hash
all go through this hashing algorithm to figure out what the
hashes okay? So if we go to over here, you know, for example, if
I type in Patrick, obviously, this is now no longer valid,
right? Because this nuns combined with the block the data
in the previous hash, aren't going to solve our problem of
having fou
r zeros at the at the start, right. So I'm gonna go
and fix that. And that's, that's kind of an easy way to see it
being broken. But, but let's take a look, if I break this
block, right here, what happens if I, if I break the data in
here, if I do like Patrick in here, you can see that both of
these are now read, both of these are now invalid, right?
Because the block hash with the nonce hash with the new data,
which is my name, Patrick has hashed with the previous block
is now a brand new hash,
right, and this block is still pointing
to this previous hash right here, right is pointing to this
previous block. And now it is wrong, and it is messed up and
now, and now it's nuts with this previous hash is also wrong.
Right? And this is where when we talk about blockchains, being
immutable, this is exactly how it's immutable, right? Because I
go back and I change anything, you know, if I've just typed a
right here, the entire blockchain is now invalidated.
Because none of these are going t
o have nonces that solve this
equation anymore. So this is why blockchains are immutable is
because anytime you change one thing, you ruin the rest of the
blockchain, okay? So however, though, you know, if it was
here, originally, we can go ahead and mine these, mine all
these but as you can see, you know, this is going to start
getting very computationally expensive, because I have to go
redo basically the entire blockchain. And the farther and
farther down the line you get, the harder and hard
er it becomes
to, you know, rehash and redo all these different block chains
here. Now, this makes a lot of sense, right? So we have this
blockchain, it's really hard to change something in the past,
but if we do, we can just go ahead and remind it. Now if I'm
the one who controls the blockchain, right, if I'm the
one who controls this, you know, and I want to change something,
the past will, okay, great. All I got to do is change the state
of here. And then you know, mine, each one of these, yo
u
know, obviously, it's going to be very computationally
expensive, but it's something that I can do right if I'm the
one who owns the blockchain. Now, here's where the
decentralized nature or the distributed nature really makes
it incredibly powerful. So we're gonna go to the distributed tab
here, which is also referred to as the decentralized tab here,
and it's going to show us what a blockchain looks like in a
decentralized manner. So we have this exact same initial setup
here we have to Shoo
t a blockchain, we have our first
blockchain, which is kind of exactly as the one from here.
But we also have more than once we have peer, a peer beam, and
PRC and when people are talking about Peer to Peer, peer to peer
transactions through the talking, this is kind of that
concept that they're talking about, right. So we have a
number of different peers who are running this blockchain
technology, they're all weighted equally, right, each one of
these peers or each one of these nodes, each one
of these
entities running a blockchain has the exact same power as
anybody else, right. So the way that we can tell very easily
which blockchain is correct, or which ones are correct, or by
looking at this end, hash here, right, or by looking at where we
are in the blockchain, because again, remember, because again,
remember this, this hash that this this in this last block
here, is going to encompass all of the blocks from before,
right, because this last hash is going to have the previous hash
here, which includes the previous hash here, which this
hash includes the previous hash here. And so this last hash is
encompasses everything in here, right? And we can look, we can
look at the hash of Piercey, which is four zeros, and then E
four B, we can look at the latest hash appear B, which is
four zeros, E for B, and then pure A, which is four zeros, E
for b. So all of these peers, all of these nodes, all of these
decentralized, you know these independent, all these
independent users run
ning this blockchain software, they're all
matched up, it's very easy for their nodes to look at each
other and say, hey, great, we are all matched up. Now, what
let's say that a decides that, you know, something happened on
the blockchain that they didn't like, and they wanted to go back
and change something, right. So let's say they change here, you
know, obviously, the rest of their blockchain is invalidated.
And they have to spend a lot of computational power to catch up
to speed. So let's g
o ahead and humor it. Let's say that they
did, they ended up catching up. They ended up catching up, you
know, they ended up mining everything. And now they have a
valid blockchain. Right? It solves the equation. Awesome.
However, in block number three, there's something new, right?
This is here, and it shouldn't have been here, this is some
that Peer A put in by themselves. All that happens now
is we look at all the blockchains that are running the
software, and we're looking at all the hashes
and hash at block
number five. So pure A has this new hash. Now, there's a 09 BC.
But pure B has a different hash 00, e for B, right? So who's
right? Is it disappear a with their new stuff? Or is it pure
B? Well, that's where the decentralized data comes in.
Because then we can look at Piercey Piercey, also as E
forby. So if you're being Piercey will say, Hey, you're a,
you're wrong, get out, right. And pure A will stop being able
to participate in the mining rewards because they have
essentiall
y forked the blockchain and started their own
little blockchain right with their own history, because
they're the only ones with this, this piece of data in block
three, whereas pure B, and pure C have nothing in there. So that
really shows why in these blockchain worlds in this
decentralized world, there really is no centralized entity,
you know, pure A, you know, might have been maliciously
motivated to change. You know, there's this block number three,
however, democracy rules, right, the maj
ority rules in the
blockchain, pure vmpfc will say, hey, you know, the, that's cute
and all puree. But you're wrong, right? That's not right. Now, it
might be a little abstract, that you just look at data and you
know, as typing kind of random stuff in here and think, okay,
yeah, that's, that's data, right? That makes sense, you
know, just kind of random strings in here doesn't really
do anything for us. So if we actually go over to the token
section here, this is where everything really starts
to make
a lot of sense. So we have the exact same setup here with pure
a pure B Piercey. Except and the difference is, instead of having
kind of this, this data section, we have this TX This transaction
section, right? And this represents all the transactions
that are happening in this block, right? So we're sending
$25, from Darcy to Bingle, or to Bingley force toward dollars and
27 cents here. 1922, right. And it's the exact same thing. So
this, all these transactions are gonna get hashed in t
he exact
same way that the data is going to get hashed. And, and this is
why it's so powerful, because again, you know, if I want to be
malicious, right, if, if I want to say, hey, I really wanted to
give Jane a lot more money from Elizabeth, so I'm puree and I go
back and I change it to 100. Well, now, you know not only do
I does my whole blockchain get invalidated because that was so
so long ago, but I'm not going to match any of these other
chains. Right? And so my blockchain is going to be
e
xcluded from the overall blockchain. So and let's let's
go ahead and fix this. And it's the same thing if down here if I
become malicious, and I want to send you know, I want Miss Audrey to have less money.
Maybe I want to send $1 And they go had in mind it the same thing
here, this hash now this two a one is not going to match the
rubies rubies hash of BBA. And it's not going to match Pierce's
hash of BBA as well. So the two of them are gonna say, hey,
this, your blockchain is invalid, it's not
matching the
majority, you know, you're out, right. So that's really how
these blockchains work at a low level. And it all goes back to
this, this understanding this hash idea, and using it in this
very sophisticated manner, to kind of cryptographically prove,
you know, where, where stuff lies. Now, the way the
blockchain works is, instead of random stuff, put in the Status
section, it's actually going to be solidity code in here to
finding ways to interact with different blocks and different
p
rotocols that are on chain, or, as we've said before, different
smart contracts. Now, the next question that you might be
asking is, okay, well, how do I know how can I be sure that I'm
the one? You know, let's say this is, let's say, I'm Darcy
right? How can I be sure that I was that Darcy was the one to
actually send us money here. How do we know that Darcy sent $25.
To Bingley? Well, this is where we get into private keys and
public keys. And that's what we're going to go into. Now.
Let's jus
t do a quick recap of what we've learned in this
section. So far, right? We've learned that Aetherium actually
runs on this hit check 256. But we use Sha 256. For this demo,
it doesn't really matter. We're just talking about hashing
algorithms. So again, hash is a unique fixed length string meant
to identify any piece of data. A hash algorithm or a hash
function is a function or algorithm that computes any type
of data into a unique hash. Mining is going to be the
process of finding the solution
to the blockchain problem. In
our example, the problem was finding a hash that starts with
four zeros. nodes get paid for mining different blocks. And the
problem is going to be different blockchain to blockchain a block
and a blockchain is basically a combination of a block, nonce
transaction and previous hash to create this unique hash for this
block. And again, depending on the blockchain implementation,
this might have a couple other fields or might have different
fields. But this is essent
ially what's going on blockchains are
decentralized and distributed because many independent users
are going to run this blockchain software. And they will check
and then we'll compare against each other to see which
blockchains are acting honestly, and which ones are acting
maliciously, in the blockchain world majority rules. The nonce
here is the answer used or the number used to get this hash.
Now nonce is kind of an overloaded term, it's actually
used for a number of different reasons. In th
is case, we're
using it to solve this problem of getting four or five zeros at
the stop or the hash. However, in Aetherium, it will also be
often used as the number of transactions from a given
address. So now we're going to talk a
little bit about signing these transactions and private keys
and some other cryptography pieces, right? Because in this
blockchain demo here, we can see we have all these these
fantastic transactions, right? All these things went through,
but how do we know that it wa
s Darcy? Who was the one to send
$25? To bangli? Right? How do we know that actually happened. And
this is where all those pieces that we just learned about in
our test net, in our meta mask account are really going to
start to, to come to life here a little bit here. So here we have
an example of public and private keys, okay, at the top, we have
this private key, right that was that was randomly generated. A
private key is you know, as it kind of states is a key that you
really want to keep se
cret, because you're going to be using
this as kind of your your secret password for all your
transactions where I can really pick, you know, any, any, any
private key, anything that I want. And with it, this
algorithm, or they're going to use an algorithm for Aetherium.
Bitcoin, they both use this elliptic curve, digital
signature algorithm, it's a variant of just a digital
signature algorithm. And it's going to create this this public
key, right, I'm really not going to go at all into kind of
this
digital signature algorithm. But just know it does use some of
these, some of the hash knowledge that we just learned
combined with some other pieces to kind of get this this public
here. So I'm not gonna go too deep into it. But we have this
private key that we create. And we get this public key. Now this
public key we want everybody to have access to right this is
yeah, whole world can see this, this private key, we really want
it to be private, we don't want people to see this, we're goi
ng
to use this private key as like a password to quote unquote,
digitally sign transactions, and then people can verify them with
this public key. So let's, let's see what this actually looks
like. Let's pick a random key, a more secure key, right? Because
the longer it is, the more secure it's going to be. And if
we go to signatures now, right? Let's say we have this, this
message that we want, right? We'll say hi world, right? We
want this To be the message, what's gonna happen is this
private
key that we've created, we can use to sign this data,
right? Remember how in the blockchain demo, you know, we
were kind of we were hashing stuff, right? We were using this
Shea 256 hash to get this hash. Well, we're doing something
similar. But instead of hashing, we're, we're using this digital
signature algorithm to create this message signature. Now,
what's really powerful about how this this algorithm works, is
that you can create this message signature with your private key,
but somebody
else can't derive your private key from the
message signature. And that's what makes this really, really
powerful. However, if we go to verify using this public key,
right, and so this is the this is that, Oh, 403. This is that
same public key, using this, using this public key, anybody
can verify, let's go ahead and sign again, anybody can verify
that the signature is yours, right? So you have a public a
private key, just for you. So you can sign things and a public
key that anybody can verify
something, right. So anybody can
verify this, and let's say somebody tries to fake a
transaction from you, they say, Hey, you know, this is this is
this is their transaction, all they have to do is verify that
this signature against your public key and very easily, this
whole thing turns red, because it isn't verified, right, that
the algorithm says, hey, that's wrong. So we can go ahead and
take that into transactions in this exact same way. So if I
want to send money, you know, if I want to se
nd $400, from, you
know, my address to another address, using my private key, I
can sign that transaction. And anybody else in the world can
then verify this transaction, right. And this is why when
people say Hydra keys, you know, protect your keys, this is what
we're talking about in our accounts here. Right? If we go
to settings, and again, the only reason that I'm showing you
guys, my pneumonic, and my private key is because this is
a, this is a dumpster account, I'm going to throw this away
at
the end of this video, or I'm just not gonna put any real
money in it. But when we look at our Metamask, here, we have this
pneumonic phrase, which allows us to easily get these different
private keys, right? So pneumonic phrase combined with,
you know, whatever account number will get us a private
key. So mnemonic phrase combined with one, we're gonna get this
private key. And this is when we look at account details, export
private key. That's where it confirm, this is
going to be the priva
te key that we're going to use to sign our
transactions, right, this, if anybody else gets access to this
private key, they then can sign transactions for us, and they
can send transactions for us. And that's why we want to keep
these private, so that it works the exact same way, right. And
so this is why it's so important to hide your private keys and
hide your mnemonics now, your Aetherium address is actually a
piece is actually a piece of your public key. Now, to get our
address in Aetherium,
all we have to do is take this public
key that we've created with our private key, hash it using that
same Aetherium hashing algorithm, and then take the
last 20 bytes. And that's how we'll actually derive to our to
our address here. Now, knowing the exact methodology of how to
get the address doesn't really matter, because it could change
blockchain to blockchain and could even change it too. But
just know that that is essentially how kind of these
addresses are derived or there's some derivat
ive of the public
key, right, because the public key is public. And you know,
using the public key and kind of any public way is totally fine,
but not the private key. So that is how we sign our transactions.
Note though, this isn't how we send the transaction. So so this
is just going to assign it create a transaction for us to
send, we'll learn later on how to send these transactions.
Cool. So that was a lot of information there too. Let's do
a quick recap, your public key is derived by using
a digital
signature algorithm on your private key, right, and you want
to keep your private key private at all times, because you're
going to use your private key to sign transactions. Signing
transactions with your private key, you are the only one who
can actually do this because you can't get the private key from a
message signature. However, using your public key, you can
anybody can very easily verify that a signature that signed by
you is in fact signed by you in our meta mask. Our private
keys
are located in this account details section you just hit
show private keys and type in your password. And you'll get
your your private key here. A quick note here is oftentimes
when using your private keys somewhere, they want it in
hexadecimal form. So if we're going to use our private key for
something like brownie, which we'll go into later, we need to
actually append a 0x to the front but We'll get into that
later. And the address of your account is derived from this. So
if you think a
bout your private key creates your public key,
which then can create your address. And there's a little
barrier or a big barrier here. Because your private key, you
want to keep private and your public key and your address can
all be public information. Now that we know a little bit more about what's going on
underneath the hood of these blockchains, let's go back at
our transactions and look at this gas thing again, and we'll
look to see what's actually happening here, gas in
particular can be
a little bit tricky to wrap your head around.
So if you don't get it right away, don't worry. As we go
through examples, it'll start to make more sense. So before I was
saying, let's just look at this transaction fee bid, which is
the costs associated with running this transaction. If I
scroll over this on ether scan, I can see this thing that says
block base fee per gas plus max party fee per gas times the gas
use, which might be a little bit confusing here, let's actually
break down what's goi
ng on on Aetherium with ERP 15, five nine
in place. And again, this is going to be specific to
Aetherium, as every blockchain might do it a little bit
differently. But if we click to see more, we can see a number of
useful values here, we can see gas limit is 21,000. And usage
is 21,000. So this transaction used 21,000 gas, and we sent
21,000 gas along with it. Sometimes when sending a
transaction, depending on when it's sent. And depending on what
the specific instructions are, it might actuall
y use way more
gas than what you want it to use. So with your transactions,
you can actually set a limit, hey, I don't want to use more
than x amount of gas, I don't want to do more than x
computational units. And in fact, we go to our Metamask. And
we click Send to transfer between accounts again, and we
pick you know, 0.01 eath, or something next can actually hit
this little button here, go to Advanced, and we can actually
edit some specifics of this transaction, one of them is
going to be the
gas limit, we can change this gas limit to
maybe 2200 2300, or more or even less, since sending Aetherium
takes exactly 21,000 Gas Metamask just defaults to
setting into that. Well, we also see these other interesting
things, we see a priority fee, and a max base fee. Let's reject
this transaction. And let's look back at ether scan to talk about
these. So currently in Aetherium, according to EE IP
1559, every transaction on Aetherium comes with something
called the base fee. This is the minimum
gas price you need to
set to include your transaction. And you'll notice that these are
priced in something called gateway. So what is a gateway?
If we come to the site eath converter.com. And again,
there's a link to this in the GitHub repository, we scroll
down we can see way gateway and ether five put one ether in
here, I can see how much one ether is in terms of way. And in
terms of way, one ether is equal to 1-234-567-8990. So that's
that's 1 billion way is going to be one ether. And then
1-234-567-8910 1112 1314 Did 16 Seven, eight team and then 18
zeros is away. These are just easier ways of referring to
really, really small amounts of Aetherium. So if we look at our
gas fees, we see that the base fee is 0.00000004 Go away. And
this obviously would be an even smaller number if this was in
units of weigh. So if we take this number, and we put it into
our calculator, we can see that this is equal to 40 weigh or
0.0000 a whole bunch of zeros for ether. The max fee here
refers to t
he maximum gas fee that we're willing to pay for
this transaction. And you can actually see that our max fee is
a little bit higher than what we actually ended up paying. Our
maximum was 2.2132 something something and the gas price we
actually paid was up here. Now your transaction might of course
be a little bit different than Additionally we have a max
priority fee. This is going to be the max gas fee that we're
willing to pay plus the max tip that we're willing to give to
miners. Now currentl
y in Aetherium, this base fee ends up
getting burnt and we can see on ether scan exactly how much is
getting burnt here. And if we pull up our calculator again, we
can grab this gas fee, multiply it by the amount of gas we used,
and we can see that this is indeed how much Aetherium we
actually ended up burning. We go back to Ethereum converter,
paste it in we can see that these two numbers are indeed
equal. This means whenever you send a transaction, a little bit
of Aetherium is removed from cir
culation forever, or it's
considered burnt. So currently, in theory As part of your
Aetherium part of your transaction fee actually gets
burnt. And then the other part goes directly to miners. So to
figure out exactly how much went to miners, we could do this
number minus the burnt amount. And this is how much Etherium
was paid to Aetherium miner for this transaction, you'll see
down here your transaction type to ERP 15, five, nine, this is
the eip 15 five nine version of these transactions. Lik
e I said,
every blockchain is going to have a different fee burning and
fee and gas process. And they're all going to be a little bit
different, but the some of it is blockchains have limited block
space for transactions, the gas price that costs for your
transaction to be included in one of these blocks changes
based off how much demand there is the base gas fee for
Aetherium will go up and down depending on how many people are
sending transactions and how many people want to be included
in a b
lock. If a ton of people want to be included in a block.
That means a ton of gas is obviously going to get burnt.
We've left a link to a video in the GitHub repository with this
section from this YouTuber who does an amazing job breaking
down this EIP 15, five, nine and more about how this gas model
actually works. I highly recommend you pause this video
and watch that video. To understand more, the base fee
gets programmatically algorithmically adjusted to try
to target for all the blocks to be
50% full. If they're more
than 50% full, this base fee automatically goes up. If
they're less than 50% full, this base fee goes down. Now this is
a lot of the basics of how this transaction works. And it can be
a little confusing. So let's do a quick refresher of everything
in here. There's a unique transaction hash that uniquely
identifies this transaction. On this blockchain, we can see the
status, we can see the block number that it's confirmed on.
One other thing we want to look at. If we s
croll up, we see
block number and block confirmations. This is how many
blocks have been mined. Since this block was included. Like we
saw with our blockchain demo, the longer the blockchain gets,
the harder it is to tamper with and the more secure it is
typically, you'll see some processes say they'll only do
something after 20 Block confirmations, 30 Block
confirmations or etc. The reason that they wait for these block
confirmations is because they want to make sure that that
transaction is ac
tually included. And we can actually
see the block that our transaction was included in and
all the other transactions with it, different details about how
much gas was used, the gas limit, etc. timestamp is when
the transaction happened, we can see from and to we can see the
value. And then we can see the transaction fee, which we see
right here is blocked base fee per gas plus the max priority
fee per gas times the gas used. And we see all the details of
the gas down here gas price is the cost
of one unit of gas gas
limit is the max amount of units of gas that we're willing to pay
in this transaction, the usage is how many actually got us the
base fee is going to be the base network fee per gas. So 40 way
per one gas used, the max gas is the max gas price we're willing
to pay. And Max priority is gonna be the max gas price, plus
the tip that we give to miners, and then we can see how much is
burnt. And then we see transaction savings which which
is the difference between how much was
actually used or paid
for and then returned. So for example, in this transaction,
the gas price we ended up picking was a little less than
our max gas price here. So the gas price we ended up using was
a little less than our max priority fee here. So we had
some savings compared to that, we can also see that this was an
ERP 15 five nine transaction, we can see our nonce here, which
was not zero because the transaction that I'm showing is
our first nones. And then of course, we can see the input
data for transactions that are just sending Aetherium, the
input data is going to be blank. But you'll see that when we get
to smart contracts, the input data is not going to be blank.
And it's going to be one of the most important features of these
transactions. You'll also notice that there's a state tab. This
is an advanced tab, and it shows the different states that are
changed based off of this transaction. We're going to
ignore this one for now. Now that we know how the blockchain
itself
works under the hood, let's talk about some blockchain
fundamentals. And we actually covered all these topics in a
previous Freako camp video. So let's go to that. If the first time you listen to this, some of
these concepts seem a little bit hard to grasp. Don't worry about
it. As we continue and as we move on with this course,
they'll start to make more sense when you see them used in real
examples. I definitely would recommend going back and
rewatching and re listening to the parts that you d
on't quite
get an asking questions in the discussions tab of the GitHub
repository. Awesome. So now that we know all the cryptography
pieces and all the little nitty gritties of how the blockchain
actually works, and how our signatures work and how
everything sticks together. Let's talk a little bit about
how this works, in actuality, and what's really going on. Now
for a lot of this, each different blockchain has
slightly different algorithms and slightly different metrics
and criteria for doin
g a lot of this stuff. So when we're
talking about these specific implementations, keep in mind,
the exact algorithm might be a little bit different, but the
concepts are all still going to be exactly the same. Hashing and
hash function is going to be the same. No matter where you look
at decentralized blockchain, it's going to be the same no
matter where you look, how it's actually implemented, is going
to be a little bit different. Now traditionally, when you run
an application, you will be we
bsite or something that
connects to some server, you are interacting with a centralized
entity. And unlike how we saw with the blockchain with
multiple different peers, it's going to be run by a single
centralized group. Now, it still could be run on many different
servers, but all those servers are still going to be controlled
by the same centralized group blockchains, as we saw run on a
network of different independent nodes. When we saw a peer, a
peer, B Piercey. Those were different examples
of different
independent users running the blockchain technology on their
own node. Now, when I use the term node, I'm usually referring
to a single instance of a decentralized system. So when I
say a single node, when I'm talking about a blockchain, I'm
talking about one of those pure A's pure BS pure C's running
that blockchain software, I'm talking about one server running
this technology. And again, it's this network. It's this
combination of these nodes interacting with each other,
that cr
eates this entire blockchain. What makes these so
potent too, is that anybody can join the network. And that's why
there's decentralized the barrier to entry is a little bit
of hardware requirements for getting the correct materials to
run the software. And then you running the software, anybody
can join these networks and participate. And that's what
makes it truly decentralized. In fact, you can go to GitHub right
now, and run your own Aetherium node in a few seconds. Now in
the traditional wo
rld, applications are run by
centralized entities. And if that entity goes down, or is
maliciously bribed, or decides that they want to shut off, they
just can't, because they are the ones that control everything.
blockchains, by contrast, don't have this problem. If one node
or one entity that runs several nodes goes down, since there are
so many other independent nodes running that it doesn't matter,
the blockchain and the system will persist so long as there is
at least one node always runnin
g. And luckily for us,
most of the most popular chains like Bitcoin and Aetherium, have
1000s and 1000s of nodes. And as we showed in our demo, if one
node acts maliciously, all the other nodes will ignore that
node and kick that out or even punish it in some systems,
because they can easily check everybody else's node and see,
okay, this one is out of sync with the majority. And yes,
majority rules when it comes to the blockchain. Each blockchain
keeps a full list of every transaction and inter
action
that's happened on that blockchain and we saw if a node
tries to act maliciously, then all their hashes are going to be
way out of whack and they're not going to match everybody else.
This gives blockchains this incredibly potent immutability
trait where nothing can be changed or corrupted. So in
essence, we can think of a blockchain as a decentralized
database. And with Aetherium, it has an extra additional feature
where it also can do computation in a decentralized manner. Now
let's tal
k consensus, proof of work and proof of stake because
you've probably heard these before. And they're really
important to how these blockchains actually work. We
went through that blockchain example, and we did that mining
feature. This is what's known as proof of work. Proof of Work and
proof of steak fall under this umbrella of consensus and
consensus is a really important topic when it comes to
blockchains. Consensus is defined as the mechanism used to
reach an agreement on the state or a sin
gle value on the
blockchain, especially in a decentralized system. I briefly
alluded to this consensus mechanism in our blockchain
example, when I said if one change is something and the
other two, don't, then majority will rule and kick that one out.
This is part of that consensus mechanism. Now very roughly a
consensus protocol in a blockchain or decentralized
system can be broken down into two pieces, a chain selection
algorithm, and a civil resistance mechanism, that
mining piece that we wer
e doing, or where the proof of work
algorithm is what's known as a civil resistance mechanism. And
this is what Aetherium and Bitcoin currently use. Please
note that depending on when you're watching this, if eath
two is out, then it's no longer proof of work. Now, proof of
work is known as a civil resistance mechanism, because it
defines a way to figure out who is the block author, which node
is going to be the node who did the work to find that mine and
be the author of that block so all the o
ther nodes can verify
that it's accurate civil resistance is a blockchains
ability to defend against users creating a large number of
pseudo anonymous identities to gain a disproportionately
advantageous influence is over set system. And in layman's
terms, it's basically a way for a blockchain to defend against
somebody making a bunch of fake blockchains so that they can get
more and more rewards. Now, there are two types of civil
resistance mechanisms that we're going to talk about here. Namely
proof of work and proof of stake. Let's talk about proof of
work a little bit more in depth first, in proof of work. This is
civil resistant, because a single node has to go through a
very computationally expensive process called mining, which we
demonstrated earlier to figure out the answer to the
blockchains Riddle of finding that correct nonce, or, or
whatever the blockchain system has in place.
And proof of work. This works because no matter how many
pseudo anonymous accounts you make, each
one still has to
undergo this very computationally expensive
activity of finding the answer to the proof of work problem, or
the proof of work riddle, which again, in our demonstration, it
was finding a nonce with that first four zeros. But again,
each blockchain might change the riddle work or change the
problem to be a little bit different. In fact, some of
these blockchains make this riddle intentionally hard or
intentionally easy to change what's called the block time,
the block time is how
long it takes between blocks being
published. And it's proportional to how hard these algorithms
are. So these problems actually can change. Depending on how
long they want the blockchain to be. If a system wants to block
time to be very, very long, they just make the problem very, very
hard. If they wanted to be very short, they make the problem a
lot easier. We'll talk about civil attacks in a little bit
and how they can affect the system. But with proof of work,
it's a verifiable way to figu
re out who the block author is and
be civil resistant. Now, you need to combine this with a
chain selection rule create this consensus. Now, there's some
consensus protocols that have more features, but very, very
roughly, these are the two pieces that we're going to look
at. The second piece is going to be a chain selection rule. How
do we know which blockchain is actually the real blockchain and
the true blockchain now on Bitcoin and Aetherium, they both
use a form of consensus called Nakamoto
consensus. And this is
a combination of proof of work and longest chain rule, the
decentralized network side that whichever blockchain has the
longest chain, or the most number of blocks on it is going
to be the chain that they use. This makes a lot of sense,
because every additional block that a chain is behind, it's
going to take more and more computation for it to come up.
That's why when we saw in our transaction, we actually saw
confirmations. The number of confirmations is the number of
a
dditional blocks added on after our transaction went through in
a block. So if we see confirmations as to it means
that the block that our transaction was in has two
blocks ahead of it in the longest chain. Now, I do want to
point out that a lot of people use proof of work as a consensus
protocol. And I do want to say that this is a little bit
inaccurate, but sometimes people use it interchangeably. Proof of
Work is a piece of the overall consensus protocol, which in
Bitcoin and Aetherium. One c
urrent case is Nakamoto
consensus, Nakamoto consensus is a combination of proof of work,
and this longest chain rule, both equally and very, very
important. Now, proof of work also tells us where these
transaction fees and these block rewards go to remember how when
we made this transaction, we had to talk about gas and a
transaction fee. So who's getting paid who was getting
this transaction, and this transaction fee is going to the
miners or the validators in a proof of work network? They're
c
alled miners and in the proof of stake network, they're called
validators there are a little bit different. And we'll get
into that when we talk about proof of stake in this proof of
work system. All these nodes are competing against each other to
find the answer to the blockchain riddle. Remember, in
our example, it was to find a hash that has four zeros at the
start. And again, depending on the blockchain implementation,
that riddle is going to be a little bit different. But all
the nodes are
trying as many as possible to try to get this
answer first. Why? Because the first node to figure out the
answer to the blockchain real is gonna get that transaction fee,
they're gonna get paid from that. Now, when a node gets
paid, they actually get paid in two different ways. One is going
to be with a transaction fee. And another piece is going to be
the block reward. Remember how we talked about alternating the
gas price or the gray on our transaction? Well, that's the
transaction fee that we
're going to pay to these blockchain nodes
for including our transaction, the block reward is given to
these nodes from the protocol from the blockchain itself.
You've probably heard of the Bitcoin halving before the
halving is referring to this block reward getting cut in half
and it's supposed to be cut in half, roughly every four years.
This block reward increases the circulating amount of whatever
cryptocurrency that is being rewarded. For example, on
Aetherium the block reward is giving out
Aetherium and a
Bitcoin the block reward is giving out Bitcoin. So these
nodes are competing against each other to be the first one to
find this transaction to be the first one to find the answer to
this problem, so that they can be the ones to win both this
block reward and your transaction fee. Some block
chains like Bitcoin, for example, have a set time when
they're no longer going to give out block rewards and the miners
or the nodes are only going to get paid from trends. Action
fees. Now
this gas fee, again is paid by whoever initialize the
transaction. When we got our funds from the faucet, there was
some server and somebody else was paying the transaction fee
for us. However, when we sent ether from one account to
another, our first account actually paid some transaction
fee to send that ether. In proof of steak. There's also a gas
fee, but it's paid out to validators instead of miners.
And we'll talk about that in a little bit. Now let's talk about
two types of attacks that c
an happen in these blockchain
worlds. Let's talk about the first one being the Sybil
attack. The Sybil attack is when a user creates a whole bunch of
pseudo anonymous accounts to try to influence a network. Now,
obviously, on Bitcoin and Aetherium, this is really,
really difficult because user needs to do all this work in
proof of work or have a ton of collateral and proof of stake,
which again, we'll talk about in a bit. The other more prevalent
attack is what's known as a 51% attack. Now, as w
e saw as part
of our consensus protocol, these block chains are going to agree
that the longest chain is the one that they're going to go
with, so long as it matches up with 51% of the rest of the
network. This means that if you have the longest chain, and you
have more than 51% of the rest of the network, you can do
what's called a fork in the network, and bring the network
onto your now longest chain. Now Sybil attacks, obviously, are
when a single node or a single entity tries to affect the
d
ecent reality of the network by pretending to be multiple
different people, although they're just the same person or
entity. And like I said, it's really difficult to do in proof
of work and proof of steak. So you can see now that blockchains
are very democratic, whichever blockchain has the most buy in
and is the longest is the blockchain that the whole system
is going to corroborate. When nodes produce a new block and
add to the longest chain, the other nodes will follow this
longest chain tha
t the rest of the network is agreeing with,
add those blocks to their chain and follow up. So very small
reorganizations are actually pretty common when a blockchain
picks a block from a different longest chain puts it on and
then has to swap it out for another block and continue with
a different blockchain. However, if a group of nodes had enough
nodes or enough power, they could essentially be 51% of the
network and influence the network in whatever direction
that they want it. This is what's
known as a 51% attack.
And it's happened on blockchains like Ethereum classic, which is
not Aetherium. This is why the bigger a blockchain is, the more
decentralized and the more secure it becomes. So after you watch this video,
and you become a blockchain engineering expert, I definitely
recommend you run a node as well, because you are going to
increase the security of the network as a whole by running a
node. So proof of work is fantastic because it allows us
to very easily protect against th
e Sybil attacks and keep our
blockchain is decentralized and secure. However, it has some
drawbacks as well. Proof of Work costs a lot of electricity,
because every single node is running as fast as they can to
win this race to get the rewards. This leads to obviously
an environmental impact. Now since proof of work and Nakamoto
consensus, a lot of other protocols have taken this idea
and gone in a different direction with a different civil
resistance protocol, a lot of them with the intention t
o be a
lot more environmentally friendly. And the most popular
one right now is proof of stake. There are some chains that are
already using this proof of stake protocol, and that are
live and thriving. Some of them are like avalanche, LaLana,
Polygon, polka dot and Terra and additionally Aetherium is
decided to upgrade to eath. Two, which will have this proof of
stake algorithm as well, it will also have some other features,
which we'll talk about in a bit. Now as a quick aside, all the
tools t
hat we're going to learn here are still going to work in
eath. Two, so depending on when you watch this, everything here
is still valid. So let's talk about proof of stake. Now,
again, this is a different civil resistance mechanism. Instead of
solving this difficult problem, proof of stake nodes put up some
collateral that they're going to behave honestly, aka, they stake
in the example of Aetherium. two nodes put up some Aetherium as a
stake that they're going to behave honestly in the network,
if they misbehave to the network, they are going to be
slashed or remove some of their steak. Obviously, this is a very
good civil resistance mechanism. Because if you try to create a
whole bunch of anonymous accounts, then each one of those
accounts, you have to put up some stake. And if you
misbehave, you're going to run the risk of losing all the money
that you put up as collateral. In this system, miners are
actually called validators because they're no longer
binding anything, they're actu
ally just validating other
nodes. Now, unlike proof of work, which every node is racing
to be the first one to find the block, and proof of stake nodes
are actually randomly chosen to propose the new block and then
the rest of the validators will validate if that node has
proposed the block. Honestly, as we saw with our cryptography
lesson, it's usually very easy for other nodes to verify if a
proposal or a transaction is honest. Now randomness is a
really important topic when we're talking abou
t blockchains.
Because keep in mind, these blockchains are deterministic
systems. They're walled gardens from the rest of the world. And
as you know, a deterministic system by definition can't have
random numbers. So how do we choose the random validators in
the system? While it changes from blockchain to blockchain,
and actually choosing the node will change blockchain to
blockchain, but eath two, they're using what's called Rand
doubt, at least for the original implementation. This is a
decent
ralized autonomous organization that collectively
chooses the random number and collectively chooses which node
is going to run. Next, we aren't going to dive too deep into this
because there's a good chance that this might change in the
future. But we will go into randomness solutions and
blockchain later on in this course. Now, proof of stake
obviously has some pros and cons as well, pros are that again, it
is a great civil resistance mechanism. And a great way to
figure out who the author of
a block should be. The other pros
are that it's way less computationally expensive to
figure out the new block, because instead of every single
node on the network trying to do this, only one node needs to do
this. And then the rest of the nodes just need to validate it.
The cons are that it's usually considered a slightly less
decentralized network, due to the upfront staking costs it
cost to participate. Now, this gets into a little bit of a
philosophical battle on how decentralized is decentr
alized
enough. And I think that's up to the community to decide. And as
we progress, I think we'll learn more and more about how
decentralized is decentralized enough. The general consensus
amongst blockchain engineers, though, is that proof of stake
is very, very decentralized and very secure. This massive
environmental impact improvement is one of the two main reasons
why eath is shifting to eath. Two, it reduces the
environmental impact by up to 99%. Now, these are the main
pieces of proof of
work and proof of stake. But I did want
to talk about another concept that's really important in these
ecosystems. And that is scalability. When we were
talking about gas prices, we were saying that the gas prices
can get really high if a lot of people want to send a
transaction, because a block only has so much black space,
and the nodes can only add so many notes. So when a lot of
people want to use a blockchain, the gas price skyrockets. This
is not very scalable, because if we want to add m
ore and more
people to these blockchains, it's going to cost more and more
to use the blockchains. Because more people are going to want to
get into these blocks. This means that there's kind of a
ceiling to how many people can use the system because of the
financial constraints that will get imposed as gas prices keep
rising. Aetherium too is not only attacking the environmental
impact of proof of work by switching to proof of steak, but
they're also implementing this new methodology called sha
rding.
And sharding is a solution to the scalability problem, a
sharded blockchain really just means that it's going to be a
blockchain of blockchains there's a main chain that's
going to coordinate everything amongst several chains that hook
into this main chain. This means that there's more chains for
people to make transactions on effectively increasing the
amount of block space that there is sharding can greatly increase
the number of transactions on a blockchain layer one now there's
anothe
r term that might be the first time you heard it, a layer
one, we're going to talk about layer one and layer twos in
terms of scalability really quickly as well. A layer one
refers to any base layer blockchain implementation
Bitcoins, a layer one Aetherium the layer one avalanches, a
layer one, these are the base layer blockchain solutions. A
layer two is any application that is added on top of a layer
one added on top of the blockchain. Some examples of
layer twos are going to be chain link arb
itrage, or optimism.
Arbitrage and optimism are very interesting because they are
layer twos that also look to solve this scalability issue.
Arbitrage and optimism are what's known as roll ups and
they roll up their transactions into a layer one like Aetherium,
we're not going to go too deep into roll ups and how they
actually work. But all you really need to know is that a
roll up is kind of like a sharded chain, they derive their
security from the base layer from the layer one like
Aetherium.
And they bulk send their transactions onto the
layer one, they solve some of the scalability issues by being
another blockchain that people can make transactions on, still
on kind of this base Aetherium layer. Now they're different
from side chains. Because side chains derive their security
from their own protocols, roll ups, derive their security from
the base layers. So arbitrage and optimism, for example, is
going to be just about as secure as Aetherium. There's some
fantastic guys in there t
hat go a little bit deeper into roll
ups and I've left a link in the description for you. All right,
so we just talked about a lot of stuff. So let's do a quick recap
before moving on. Aetherium and Bitcoin are currently both proof
of work blockchains that follow Nakamoto consensus, however,
Aetherium is moving to Aetherium two, which will be a proof of
stake sharded blockchain Sybil attacks are prevented due to
protocols like proof of work and proof of steak 51% attacks grow
increasingly harder
with the size of blockchain. So you
should run a node consensus is the mechanism that allows a
blockchain to agree upon what the state of the blockchain is
sharding and roll ups are solutions to scalability issues
on layer ones. Layer One is any based blockchain implementation
like Bitcoin or Aetherium. A blockchain scalability problem
is that there's not always enough block space for the
amount of transactions that want to get in them. This leads to
very high gas prices and a Again, gas prices
or how much it
costs to interact with the blockchain. So that's it for the blockchain
basics and the blockchain explainers. With just this
information, you now can go off into the world and start working
with blockchains and interacting with blockchains. With at least
some level of knowledge as to what's going on, you should be
incredibly proud of yourself for just making it this far.
Definitely be sure to give yourself a pat on the back and a
round of applause. Now that we've gotten a lot of t
he basics
and the fundamentals of the way, let's start jumping into the
coding aspect. This is where you're going to learn how to
actually build these smart contracts, how to build these
trust minimized agreements, in these blockchains. And in the
smart contract platforms. This next section, this solidity
basics, the solidity fundamentals section will give
you all the skills to start actually coding solidity and
understanding how these smart contracts work underneath the
hood. So at this point,
absolutely, give yourself a high
five, maybe say hi, in the GitHub discussions, maybe say hi
in the community, on Twitter, on Reddit, etc, and be proud of
just making it this far, the journey has really only just
begun, but you've already learned so much. Let's begin the
next section. And let's jump into the code. Now that we're getting to the
coding sections, I need to stress to absolutely use the
GitHub repository associated with this course. If you come to
the GitHub repo, and you scroll down
, and you click the lesson
that we're on right now, we're on lesson two. Welcome to remix,
simple storage. If you click on it, it'll give you a ton of
timestamps and, and other helpful links associated with
this lesson. Additionally, the biggest piece is that all the
code will be available right underneath the lesson title.
This will have all the code that we're going to be working with,
as well as some more additional information on how to work with
the code. Please, when asking questions and e
ntering in
discussions, though, please ask your questions and the full
blockchain solidity course repository. Thank you. And if
we're at the top of the repository, and we scroll down,
we have the resources for this course section. Which brings us
to the GitHub discussions in which you can ask questions in
the GitHub discussion section of this course. Additionally, on
Stack Exchange, Aetherium, or at Stack Overflow. I'll talk a
little bit about how to format questions and ask questions the
best w
ay so that you have the highest chance of getting a good
answer in a later lesson, I highly recommend you pause and
make accounts for Stack Exchange Aetherium, Stack Overflow, and
GitHub right now, if you haven't already, links to them, of
course, can be found in our GitHub repository. Typically,
for each coding section, I'll start it off by giving a quick
overview of the code of what we're going to be working with
and what we're going to be building towards, since
everything that we're doing is
going to be project based. And
that's how we're going to learn. For our first one and remix
though, we're going to skip over that because there's a lot of
stuff to get used to. Now, I highly recommend that as I'm
coding this. And as I'm doing all this in remix, you follow
along with me and you code along with me. Remember, you can
change my speed if I'm coding too fast, or if I'm coding too
slow. To start, we're going to jump into a tool called remix.
If you're unsure how to get there, there's
a link to remix
in our GitHub repository. This is where we're gonna be writing
all of our code. So welcome to the remix IDE, or integrated
development environment. This is where we're going to learn how
to code and interact with our smart contracts. If you want,
you can go ahead and accept help out remix. If you've never been
here before, it'll give you a quick walkthrough of some of the
tools that remix actually has, we're going to skip over them
for now. Because I'm gonna explain everything th
at's going
up. Remix is such a powerful tool because it has a lot of
features that allow us to really see and interact with our smart
contracts. Eventually, we're going to move off of remix,
actually to a local development environment. However, remix is
absolutely fantastic for learning the fundamentals of
solidity. And I highly recommend everybody start with remix when
they're getting started. When you come to the remix IDE,
there's a whole lot of different things that are popping out to
us. Th
ere's a lot of different plugins as well. Since we're
going to be working with solidity, which is going to be
the language that we're using to develop our smart contracts. We
can go ahead and get started by clicking the solidity plugin,
and a couple of other tools will show up on the side. Even if you
don't click the solidity plugin, you'll still be able to code
solidity smart contracts, the left hand side is where we're
going to start to actually interact with things. The button
on the top most
of the left is our files or explore
directories, remix comes boilerplate with some different
contracts, some different scripts, some different tests,
and different dependencies. We are going to minimize this a
little bit. So if you want to go ahead and right click and delete
some of these folders other than the contracts folders, feel free
to do so. Or if you kind of like them there, feel free to leave
them as well. We're going to leave our contracts folder and
we're going to delete the differe
nt files inside of it
just so that we can start From a blank slate. Most projects come
with something known as a readme. Usually it's a
readme.md, which usually explains how to actually work
with code. But for our purposes, we're going to delete this as
well. And you can just follow along with me. Now we have a blank remix Setup, click on the
contracts folder and click the little page icon to create a new
file, a little box will pop up and you can start typing text
into it. We're going to type i
n simple storage dot Sol, dot Sol
tells our compilers that this is going to be a solidity file, and
that we're going to code solidity in this solidity is the
primary coding language of smart contracts. There are a few other
smart contract languages as well. But solidity by far is the
most dominant smart contract coding language out there. And
now we have a simple storage dot soul contract on the right that
we can actually start coding our solidity with. So let's start
coding some solidity. Now i
f you click on this button right below
the files button that looks like the solidity logo, you'll see a
bunch of stuff pop up in here. These are different parameters
for us to actually compile our solidity code so that we can run
it. So the first thing that you're going to need in any
solidity smart contract is going to be the version of solidity
that you're going to use. And this should always be at the top
of your solidity code, solidity is a constantly changing
language, and it constantly upd
ating language. Because it's
relatively new compared to other languages, we need to tell our
code, hey, this is the version that I want you to use, we can
add the solidity version by doing pragma. solidity. And then
the version that we want to use, if we want to choose a very
specific version, we could say zero, point 8.7. The most
current version to date is 0.8. Point 12. But getting used to
different versions of solidity is good practice, and different
versions of solidity are considered more
stable than
others. Zero point 8.7 is one of those versions that is
considered more stable. These double slashes here are what's
known as a comment, there are places where you can type stuff
that won't actually get executed in won't get compiled and isn't
really considered part of your code. For example, I could write
Hello all. I'm Patrick. And if we were going to run this code,
this part of my code would get completely ignored. So this
double backslash is how we do what's called comments. And
as
we're coding, and as we're building our projects, be sure
to use this comments tool to your advantage every time you
write a new function, or you learn something that you didn't
understand, or you learned something new that you want to
remember, put it in a comment in your code, you're going to be
most effective at taking notes in this course, by making them
comments in your code and then saving your code so you can
refer back to it later. So leave comments in your code, leave
notes in your c
ode. And that will be one of the best ways for
you to understand what you're coding when you want to refer
back to it later. Now when it comes to the versions of
solidity, there's actually a few different ways we can actually
write it, we can say we want to use only zero point 8.7. And
this is how we would write that. But maybe we're okay if we use a
more new version of solidity than zero point 8.7 to tell our
code that we're okay with a more new version, we can put a little
caret here. And this
is how we tell solidity. Hey, any version
of zero point 8.7 And above is okay for this contract. This
means zero point 8.8 would work zero point 8.9 0.8 point 10,
etc. But if we wanted to use just 0.17, we would type in like
that if we want to use solidity versions between a specific
range, we could do something like this, we can say we want
our solidity version greater than or equal to zero point 8.7
But less than zero point 9.0. This means that any compiler
between zero point 8.7 and zero poi
nt 9.0 would work. This means
zero point 8.8 would work. Zero point 8.9 would work 0.8 point
10 would work. But zero point 9.0 would not work because it is
not strictly less than 0.9 point 00 point 9.1 would also not
work. To keep things simple for us, we're going to use zero
point 8.8. And every line of solidity that's completed, every
completed section needs to end with one of these semicolons
this is how you tell solidity it's the end of the line. Also
at the top of your code, you're always g
oing to want to put
what's called an spdx license identifier. This is optional,
but some compilers will flag your warning that you don't have
one. This is to make licensing and sharing code a lot easier.
We have a link to more about how licenses work in the section of
this lesson in our GitHub repository to do an spdx license
identifier, we just say spdx license identifier, and we're
gonna choose MIT, the MIT license is one of the least
restrictive licenses out there. So we use the MIT license f
or
most of our code samples once you have a version and once you
have Is this much written, we can actually go ahead and write
to our compiler tab and scroll down and hit Compile, that
little turn thing will go. And in a minute, we'll see, this
contract is attempted to be compiled. Since we actually
don't have a contract, we see no contract compiled yet, but we
see the compiler automatically switched to zero point 8.8.
compiling our code means taking our more human readable code
like pragma, sol
idity and transforming it into computer
code, or very specific instructions for the computer to
use. We'll go over what a lot of this machine level code or this
computer level code is doing in a later section. If you're using
a Mac, you can also hit command S, and it will run the compiler
for you as well. On Windows, it might be Ctrl S, we can actually
choose the compiler version that we want to use. However, if we
tell in our code to specifically use zero, point 8.8, and we hit
the compile butt
on, it'll automatically switch to zero
point 8.8. However, if we use the carrot thing, we get
specifically say, hey, we want 0.8 point 10, we can hit
compile, and it will compile with 0.8 point 10. Because
again, remember, the carrot says we want to use at least zero,
point eight, all the way up to the latest version of 0.8. Now
let's stay on zero point 8.8. The next thing that we're going
to do in our code is define our contract. And to get a full
screen view, you can go ahead and hit the compi
ler button to
get rid of it there. To start defining our contract, we're
gonna go ahead and write the word contract. This tells
solidity that the next pieces of code is going to be a contract
contract is a key word in solidity, and it tells our
compiler that the next section of this code is going to define
a contract. You can think of a contract similar to a class in
any object oriented programming like Java or JavaScript. Let's
go ahead and give our contract a name here, we're going to call
RS
simple storage. And then we add this little open and close
curly brackets. Everything inside this open and close curly
brackets is going to be the contents of this contract.
Simple Storage. Now, if we go ahead and hit command S or Ctrl
S, we can see this little green checkmark show up. And if you
don't, you can always go back to the compiler tab, scroll down
and hit Compile and see the little green checkmark. That
little green checkmark means that our code is compiling
successfully. And we don't
have any errors, we could
hypothetically deploy this contract right now. And it would
be a valid contract. So congratulations on writing your
first contract. Now solidity has multiple
different types or primitive data types. And if you go to the
solidity documentation, which again, is in our GitHub
repository, you can read more and learn more about the
different types that are in here. The four most basic types
are going to be Boolean, you int, int, and an address or bytes, which is a lower lev
el
type, which we'll talk about a little bit later. A boolean
define some type of true false, a you int is going to be an
unsigned integer, which means it's going to be a whole number
that isn't positive or negative. It's just positive, we have an
integer, which is going to be a positive or negative whole
number. And then we have an address, which is going to be an
address, like what we see in our meta mask here. There are some
other types as well that you'll learn later on. The reason that
we h
ave these types is we use them to define what different
variables are. Variables are basically holders for different
values. For example, we could create a variable called has
favorite number to represent if somebody has a favorite number.
And we would put this bull keyword before has to renumber
say, Okay, we have a variable called has favorite number, and
it's of type boolean. So this has favorite number is going to
represent a true or a false to set its value, we could say has
favorite number
equals true. Now has favorite number is going to
be true. We could also say has favorite number equals false. So
this Boolean has faded number is now going to be false. For uns
we could say you went favorite number equals and then set a
number 123. This means that our favorite number is going to be
123 You'll enter is special because we can actually specify
how many bits want to allocate to this number bits and bytes
are pretty fundamental pieces of information for computer
science. We're not g
oing to go over it here. However, there's a
fantastic video in the GitHub repository that explains it
more. Basically, it's how much storage or memory to allocate to
this number. How big can it get, if we say a you int eight can
have eight bits all the way up to you went to 56. If you don't
specify how big it is, it automatically defaults to you
into 256. Oftentimes, it's better when writing our code to
be very explicit. So usually you'll see me just do you int
256 to represent a un 256. We coul
d also do an int favorite
number equals 123 or an int 256. I'm just Going to go ahead and
add this Boolean back here, we're going to change this back
to UNT to 36. And let's change our favorite number to five
here, we could also do something called strings, string, favorite
number in text equals five strings represent basically
words, and you can represent them by putting them in these
quotes, it's going to be some word or phrase, or really,
really just kind of any combination of keystrokes in
h
ere, our ends can be positive or negative. So we could say,
negative five or positive five, both are going to be valid,
since we can also do address my address equals and grab our
address, right from Metamask. And paste it in, you'll notice
that we end all of these lines of code with the semicolon. We
also have bytes objects, or a bytes 32, again, representing
how many bytes we want them to be. And this says that we have
called favorite bytes, and we're just gonna set it equal to cat.
So strings
are actually really interesting, because strings are
secretly just bytes objects, but only for text to a cat is
actually a string, but can automatically get converted into
one of these bytes object bytes, objects typically look like 0x.
And then some random letters and numbers that represent the bytes
object, but cat can automatically get converted down
to bytes. We'll talk about bytes more in coming sessions, you can
also do bytes, two bytes, three bytes, five bytes 22, you get
the picture for
our uns and our into the sixth lowest we can go
is eight bits, because eight bits is a byte. And we can go up
by steps of eight. So we can do 816 32, etc, all the way to 256.
For example, down here, we can't do bytes 64. And if we go ahead
and try to compile this, we get a little red thing here. And if
we scroll down, we get a declaration error identifier not
found or not unique. Bytes 64 favorite bytes equals cats. And
we even got a little red warning sign here in our remix. This is
remix tell
ing us there's something wrong with this line.
So we can switch back to bite 32. Since byte 32 is the maximum
size that a bytes can be, you could also do just a bytes
object, which means it can have any size, but we typically want
to be explicit. And we're going to stick with bytes 32 For now
want to learn more about the different types and how to use
them and all the different features with them, be sure to
check out the solidity documentation. For now for our
simple storage, let's say we only
want to store numbers. So
let's go ahead and delete everything except for the
favorite number section. Now in solidity, if I do this, and I remove the
equals five, this favorite number actually does get set to
a default value, the default value for solidity is going to
be whatever the null value is, which in solidity is case zero.
So saying you Intuit six favorite number is going to be
the same as saying you 256 favorite number equals zero
since it gets initialized to zero. So for now, let's not
initialize it to anything. So that favorite number will
automatically start off as zero. Now, if you get confused, as
you're coding along, and you're following along with me, be sure
to write comments in your code so you know what's going on. So
maybe, for example, a great comment here would be this gets
initially alized to zero. And then if that's even confusing,
you could say, this means that this section is a comment. Now
let's go ahead and create a function functions or methods
are self con
tained modules that will execute some specific set
of instructions for us, when we call it if you're familiar with
Java, or Python, or JavaScript or anything like that functions
work the exact same way functions get identified by the
keyword function, let's create a function called store that will
change the value of favorite number to some new value. And
the number that we're going to change it to is going to be
variables that are passed to our store function here. So we're
going to allow our s
tore function to take a variable of
type un 256. And we'll call it underscore favorite number, we'll make this a public
function, which we'll get to in a minute. And all we're going to
do is we're going to set favorite number equal to
whatever variable that we just passed. So now we have this
function called store, that it takes some parameter that we're
going to give it and it sets this favorite number variable
equal to whatever number that we give this function. Now to see
this actually in act
ion, let's deploy this to an even thicker
blockchain than a test net. We're going to actually deploy
this to a local network or a JavaScript VM. And first before
we can even do that, let's just make sure that it's going
compiling correctly looks like we have a green checkmark, which
is good. And we'll come down to this button here, which is our
deploy and run Transactions tab. Our deploy and run Transactions
tab has a ton of different configuration pieces for
actually deploying this contract. Fi
rst, we want to make
sure we are on the JavaScript VM London piece here, JavaScript VM
means we're going to be deploying to a fake local
JavaScript VM. The JavaScript VM is a fake local blockchain where
we can simulate transactions really quickly without having to
wait for them to go through on a test net, don't worry about the
London versus Berlin piece here for now, injected web three and
web three provider we'll talk about in a little bit. We also
have this account section here. When we run o
n our fake
JavaScript VM, we're given a whole bunch of fake accounts
from where to deploy from, and we're given 100 eath. For each
one of these fake accounts, you can kind of think of it similar
to our meta mask account in meta mask, except for the difference
here is that this is this fake JavaScript VM Aetherium that
we're given. For our transactions, including
deploying contracts, we're actually given a gas limit,
there's also values we can send, and we can choose our contracts.
Right now we o
nly have one contract, simple storage, so
that's going to be the one that we're going to deploy. So on the
left hand side, to deploy this to our fake JavaScript VM, we're
gonna go ahead and hit the Deploy button. And if we scroll
all the way down to the bottom, now, we can see a contract was
deployed. It says simple storage at x, blah, blah, blah, blah,
blah. And we see this orange button store with come this
grade text you in 256, underscore favorite number on
our fake local blockchain, we're a
ctually given an address every
single smart contract, it has an address, just like how our
wallets have an address. So if we hit this copy button here,
and we put it into a comment, make this a little bit bigger,
we can see that the address of this contract that we just
deployed, is located at this address. Additionally, if you
pull up the slider over here, you'll be able to see this
little green checkmark with all this information about this
deployment. And you can hit the little drop down and
see a whole
lot more information about this. Something you might notice is
you'll see some familiar keywords like status,
transaction hash, from to gas, etc. When we deploy a contract,
it's actually the same as sending a transaction. Remember,
anytime we do anything on the blockchain, we modify any value,
we are sending a transaction. So deploying a contract is
modifying the blockchain to have this contract, it's modifying
the state of the blockchain. And if we had sent this on a
Rinkeby, or COV
ID, or main net network, we would have had to
spend the gas to actually deploy this contract. And this is the
simulation of how much gas and the transaction hash and from
and to and all this other stuff about our transaction had, we
actually deployed it to a real network. But since it's
JavaScript VM, it's all fake information. Now we have this
big orange button store, this big orange button resembles the
store function that we just created. So if we add some
number into this store, like 123, an
d we hit the Store
button, we actually call this store button. And we actually
execute a transaction on our fake Jasika. Blockchain to store
the number 1234 favorite number. And if we scroll all the way up
to our account, now, you'll see that we have a little bit less
ether in our fake account. This is because we spent the gas to
actually call this contract. And if we pull up this bottom bit
here, and I call this with five I call store, you'll see it
flashed for a quick second, we sent another t
ransaction to
store the value five in our favorite number. Now the
question might be having is, that's really cool, Patrick, but
I can't see what favorite number actually is, how do I know that
those transactions are actually going through? Well, right now,
the visibility of our favorite number is set to private, so we
actually can't see it. And we'll talk about visibility in just a
second. To make it so that we can see it. We'll change our
favorite numbers visibility to public. So let's go ahea
d, we'll
recompile we'll go back to the deploy tab. We'll click the
little x here, which is to say let's get rid of this contract.
And it just gets rid of it from our window here. It doesn't
actually get rid of it from the blockchain, because again,
they're immutable, well, kind of immutable, since again, this is
kind of a fake simulated chain. But we go ahead and compile, and
now we hit Deploy again. And if we scroll down, our new
contract, will now have two buttons. One is the orange
button fo
r store. But now we have a New Favorite button. This
button represents this public variable favorite number, and it
resembles a function saying, Hey, show me what favorite
number is. So if I were to click this favorite number button,
what do you think will show up? Well, do you remember what this
gets initialized to? Well, let's click it now. We do indeed See
that zero shows up, we see that this is a YouTube ID six, and
the value stored in it is zero. Now, if I were to change that
number to five
by calling the store function, and now hitting
favorite number, we do indeed see, favorite number gets
updated to five functions and variables can have one for
visibility specifiers, we have public, private, external, and
internal. Public is visible externally and internally,
meaning anybody who interacts with this contract or sees this
contract can see what's stored in this favorite number
function. You'll see here in the solidity documentation, it says
it creates a getter function for the sto
rage slash state
variables. When we add this keyword public to favorite
number, what we're actually doing is we're creating what's
called a getter function for favorite number, we're basically
creating a function that says to return the value of favorite
number, and that's why this blue button pops up. Because this
blue button is a function that says, hey, return the value of
favorite number private means only this specific contract can
call this function. Now for storage, it doesn't mean only
t
his contract can read what's stored here. And we'll get into
that a little bit later. But by that means, this is the only
contract that can call the favorite number function.
Private functions are only visible to the current contract.
External functions are only visible externally, meaning
somebody outside this contract can call this function. And then
internal means that only this contract and it's children
contracts can actually read it, but we'll get into that a little
bit later, too. So oddl
y enough, variables are just function
calls. Now, the reason that we didn't see favorite numbers show
up on the left hand side, when we first deployed this without
the public keyword. When we don't give a visibility
specifier to functions or variables, they automatically
get deployed as internal. And as we know, internal functions and
variables can only be called by this specific contract or
derived contracts, which again, we'll get into later. So let's
just keep it public. For now, the reason t
hat we're prefixing,
our parameter here with an underscore is a way to tell us,
hey, this variable here is different from the favorite
number global, there are some different naming conventions
that are used for parameters. And as we get later into the
course, will understand more and more of what good names are
parameters are, every time we call this store function. And we
change the value here, we're actually sending a transaction
because remember, every single time we change the state of the
blockchain, we do it in a transaction. And we can see all
the details here. If you go over to the transaction details in
the logging area of your remix, you can actually scroll down and
you can see the transaction cost in units of gas, you'll see a
number of something around this. And you'll notice it's more than
that 21,000 number from sending Aetherium. That's because we're
doing something more computationally expensive. We're
actually storing a number over here. Now what do you think will
hap
pen if we do more inside of the store function as well. So
instead of just storing this number, maybe what else we do is
we will store the number here. And then we'll update our
favorite number will say favorite number equals favorite
number plus one. Since we're doing more stuff, now we should
see this store function actually become more expensive. So let's
go ahead and recompile we'll do delete this will redeploy. We
now have a new contract will store five again. Now if we look
in the details
of this transaction, and we scroll down
to execution costs, we do indeed see the amount of gas has
greatly increased. And that's because we're doing more things,
this store function is now more computationally expensive. And
like I said, each blockchain has a little different way of how
they actually calculate gas. But the easiest way to think about
it is, the more stuff you do, the more expensive that
transaction is going to cost. So let's go ahead and delete this
line to continue our example.
Now let's talk about scope for a
second, our favorite number is basically in something called
the global scope, meaning anything inside of these
brackets can access this favorite number variable. But
what if I did something like this? What if I made a un 256
called Test var? And I set it equal to five? And then I
created a new function called something will have it take no
parameters and be public? Could I access the test var and then
change it to something like six? Can we do that? Well, let's
go
ahead and see what happens when we try to compile this. We
actually run into an error. We had expected primary expression
right here. Oh, well, that's because I have the double
question mark. Let's try now. We get undeclared identifier. Our
something function doesn't know about this test var when you
create variables, they only can be viewed in the scope of where
they are. Now if that's a little confusing, just look for the
curly brackets. These two curly brackets encompass this whole
We'll s
ection here write, the opening one is up here, the
closing one is down here. So if I create a variable directly
inside of these curly brackets, that means everything in here
can access it. However, test var was created inside of these
curly brackets, which means that only stuff inside of these curly
brackets can access test var, since our functions something
isn't inside of store, or something function won't know
about test var. So that's how scope works, you want to look to
see if your variable
that you created is inside of these curly
brackets. And that's how you can know if other functions can work
with them. So this is why this fails. Now, like what we saw in
the documentation, when we add this public variable to favorite
number, we're secretly adding a function that just returns this
favorite number, we can also add our own function that also
returns the favorite number to resemble the function that's
getting created in the backend. So we can say function, we call
it retrieve, and
we make it a public view. And we'll say it
returns, you went to 56. or explain what that means in just
a second. And we'll say return favorite number. Now, I'm going
to hit command S, which again, I'm going to do that a lot
throughout this section. But just remember that that's
equivalent to me going to the compile tab and hitting compile.
Now if we go to the deploy tab, delete our last one, deploy a
new one, we now have a retrieve function, which is going to
return the exact same thing that ou
r favorite number is going to
return. Again, if we update this to five, called favorite number
and then retrieve they both now return five. Now as you can see
here, these two functions are blue, but this function is
orange. What's the difference? Why did these have these
different colors? Well, the key lies in this view keyword here,
there are actually two keywords in solidity that notate a
function that doesn't actually have to spend gas to run. And
those keywords are view, and pure. And let's
also get rid of
this variable here. A function that is a view function means
we're just going to read state from this contract, we're just
going to read something off of this contract. For example, our
retrieve function right now is just reading what favorite
number is, a view function disallows any modification of
state. So you can't update the blockchain at all with a view
function. Pure functions also disallow any modification of
state. So we couldn't update our favorite number. Not only that
,
but they also disallow reading from the blockchain. So we
couldn't read favorite number either. Instead, what you might
do with a pure function is maybe something like function, add public here, one
plus one, or return, one plus one, this
would be turns you into 36, maybe something like this, maybe
there's some math you want to use over and over again, maybe
there's some specific algorithm that you want to implement that
doesn't actually need to read any storage, etc. Now, if we
call a view fu
nction, or a pure function by itself, we actually
don't need to spend any gas. Since we're just reading from
the blockchain. Remember, we only spend gas we only make a
transaction if we modify the blockchain state. So you'll
notice in our little console down here, that if I call
retrieve this call things comes up. However, it looks different
than when we call the store function. And we call the store
function, we get this little checkmark, we get a hash, we
don't get a little checkmark. And we d
on't get a hash with the
calls. That's because clicking these blue buttons doesn't make
a transaction. This is saying, Hey, we're just going to read
off chain, we're just going to read this value. However, if you
look in the details of this call, there's this execution
cost bit here. So what's going on? Well, we can read this part
right here, cost only applies when called by contract. If we
do have a function that calls retrieve, if there's a function
that is updating state that calls a view or
a pure function,
that's the only time it'll cost gas. So for example, if our
store function which is not a view function, were to call
retrieve at some point, then we'd have to pay the cost of the
Retrieve because reading from the blockchain cost this
computation and cost gas calling view functions is free, unless
you're calling it inside of a function that costs gas, in
which case it will cost gas. So if we leave it here, we delete
this recompile redeploy. We had favorite number retrieved, they
both still cost nothing. But if we add, restore eight in here,
we can see, we can see our execution cost has gone up from
what it was without retrieve, which we can go ahead, we can
compile, I hit command us to compile here, we can deploy.
Let's go ahead and store again. We'll click on that transaction,
we can see that it's much cheaper without that retrieve
function in there. And again, our favorite number variable as
long as it has this public visible solidity, it also is
counted as a view fu
nction that returns a un 256. The returns
keyword means what is this function going to give us after
we call it so we say this function is going to give us
this function is going to return a un 256. When we call retrieve,
it's going to return or give us a utility six. This is the
result of calling the function this six is the result of
calling arbitrary function. Now our contract is good as it is, it allows us to
store a single favorite number. But what if we want to store a
range of favorite nu
mbers? Or maybe we want to store a whole
bunch of different people who have different favorite numbers?
Well, how do we do that there are several different ways that
we can approach this, one of the ways we could start approaching
this is by creating what's called a struct of people. Or we
create a new type. In our solidity, we can create a people
object that holds both someone's name, and their favorite number.
To do that, we say struct people, you went to 56, favorite
number, and a string name
. Now we've created a new type called
people kinda like you intuited six, or Boolean, or string. Now
we have a people type that we can use. Now similar to how we
created a un 256 public favorite number, we can do the exact same
thing. But with a people, we could say people public, we call
this person, and we can create a new people and assign it to this
variable person. So we'll say equals people public person,
equals, and we'll add parentheses here to signify
we're creating a new person. And si
nce we made this a struct, we
add little curly brackets here to let solidity know that we're
going to be grabbing from these struct variables, we'll say
favorite number is going to be two, and the name is going to be
Patrick semicolon, and then we can hit Ctrl S, or we can go
ahead and compile. Now if we go ahead and deploy this we now
have a new person, since this, again, is a public variable, it
has a getter function called person. And if we click person,
we see our new object, the favorite nu
mber is two. And then
the name is Patrick, you see this zero and this one, because
these are showing the index of the different variables. But
those of you new to computer science, typically in computer
science, let's start with the number zero. So what are zero
with index, we have you in 256, called favorite number, which is
saved at two. And then at index one, we have a string, which
stands for the name of Patrick, whenever you have a list of
variables inside of an object in solidity, they get
automatically
indexed. So favorite number gets indexed to zero, and name gets
indexed to one. Interestingly enough, if you have a whole
bunch of variables inside your contract, like we have public
favorite number, this favorite number, actually, technically is
getting indexed at the zero with storage slot. And if we were to
make another one of these, maybe you want to get six public
brother's favorite number, this would technically be indexed at
the first slot. And then if we were to make one m
ore, maybe
sister's favorite number, this would be the next add the second
slot. So favorite number at zero, this add one, and this a
two, but we'll learn more about that much later in the course.
Similarly, favorite number is index zero name is index at one.
Now what we have is great, but if we want a whole lot of people
are we gonna have to keep copy pasting and changing the
people's name person to their favorite one, number one, number
three, will name them ally. Person three, their favorite
number will be seven. Their name will be Chad or something. This
obviously isn't great way to create lists and large number of
people's because we have to statically keep typing them in.
So a much better way to create a list. And let's actually just go
ahead and delete Patrick to a much better way to create a list
is to use a data structure called an array. An array is a
way to store a list, or a sequence of objects, creating an
array works the exact same we've seen to initialize other
different
types. Typically, we do the type of the object, the
visibility of the object, and then the variable name. We do
the exact same thing with arrays. We'll say we want a
people array. These little brackets represent that we want
an array of people. We'll give it a visibility of public and
we'll call it people you could do the same thing with you and
256 for example, you could say you went to 56 public favorite numbers list and just add this little array
key here. And now favorite numbers list is go
ing to be an
array or a list We're going to comment that out for now. Now if
I were to go ahead and deploy this contract, let's go ahead
delete the last one, let's redeploy. We now have this blue
people button here. Remember, since it's public, and it's a
variable, it automatically is given a view function, it's
given one of these blue buttons. And instead of just having a
single button where the value shows up, it's giving us a form
to fill out, it wants to take a un 256 as an input parameter.
So
if I put zero, I get nothing back, if I put one, I get
nothing back. No matter what you put in this box, right? Now
we're gonna get nothing back. This is because our people array
or our people list is currently empty. And the value that it
wants is going to be the index of the object that you want. So
for example, if at index zero, I had Patrick, it would show
Patrick for zero, if it didn't x one I had John, or actually
better yet, to Patrick. Seven, John, etc. This is what it would show.
But
since it's empty, it's going to show nothing. And let's go
ahead and remove the public variable from favorite numbers
so that we don't get the duplicate functions at the
moment, we'll just get the Retrieve function, we'll show
you how to add to this array in just a second. This type of
array is what's known as a dynamic array, because the size
of the array isn't given at the array initialization. If we were
to say, a people array and add a three in these brackets here,
that means that this list
, or this array of people could only
be three people big, if we don't give it a size, it means it can
be any size and the size of the array can grow and shrink as we
add and subtract people, if I add three, it can only have a
maximum of three in the array ever, we're going to work with a
dynamic array, because we're going to want to add a arbitrary
number of people to this array. So let's go ahead and create a
function that's going to add people who are people array, so
we're gonna say function,
add person. And we're going to take
string, memory, name as input parameter, and I'll explain that
in a minute. And a un 256 underscore favorite number,
we're going to make this a public function, or we're going
to do is we're going to call a push function that's available
on our people object. So we're gonna say people dot push, and
we're going to create a new person, a new people object,
which is going to take in the favorite number. And the name. Now, this might be a little bit
tricky to you
. So let's break this down. People here is
capitalized. So we know that since capitalize, it's referring
to this struct people and not our variable people, the
lowercase people here is referring to this lowercase
array. So we're saying our array dot push, or push is the
equivalent of adding basically, a new people that grabs favorite
number and name. Another way that we could actually do this
is we could create a variable of type people and then add it like
so. So we could say people, new person
equals people. And then
we put those brackets the same way we did before. You say
favorite number is going to be this input value, this
parameter, and we could say name is going to be this parameter.
Now if you hit save, you'll get this error set here, saying data
location must be stored in memory, or called data for
variable but no one's given. For now, we're just going to add the
memory keyword here, and I'll explain what it does in a little
bit. And then of course, we need to add the new per
son into our
people dot push right here. So this is how we're actually going
to push people into our people array. And I'll get to this
memory keyword in a bit. Now if we go back to our deploy tab, we
delete our last contract. Let's deploy this new one. Right now,
if we try to look at the zero with person in our people array,
we get nothing but let's go ahead and add a person, we'll
call it, Patrick will be the name, and seven will be the
favorite number. So we added Patrick, we added seven. Now
if
we look at people's zero, we should see the zeroeth person
has a name of Patrick, and a favorite number of seven, boom.
And that's exactly what we do see, we see a favorite number of
seven, and we see a string name Patrick, we tried to add John
and do his 16. We hit Add Person, we can see our
transaction go through. And now if we go to people at zero, it's
still Patrick with a favorite number of seven. But if we look
at the people at index one, it's going to be John with a favorite
number of
16. And if we look at two, this, of course, should be
blank. And we do indeed see nothing actually happens here.
Perfect. Now there's actually a couple of different ways to
create this new person here, like we showed before, we can
use this bracket notation, or what we can do is we can
actually just add the parameters in the order that they show. So
the first parameter for people is going to be favorite number.
So we can just do favorite number, comma, and the second
one is going to be name, th
e second one is going to be name.
So if we save this, this, this line we just created is the
exact same as the last line, we're being a little bit less
explicit here. So the other way is generally a little bit better
because it's more explicit as to what variables are what or we
don't even need to save to this variable here, we could take out
this whole line, replace new person with exactly what we just
saw, like so now we don't even need the memory keyword. Now,
you've probably seen this by now
. But if I go ahead and
compile, and I see a little, little red one here, I roll
over. And it says something about error, expected semicolon,
but got bracket, all these errors mean that your code isn't
compiling. It's not working as expected. So now I can go over
here and do a little semicolon recompile, and I get a green.
Now if I delete this top line, for example, and I compile it,
actually get a yellow thing. Yellow stands for warnings, the
warning that I get is warning SPX license identifier
, not
provided, you should add it. So let's go ahead and add that
back. recompile. And the warning goes away. Warnings Don't stop
your code from compiling. So if you get warnings, it's okay. But
it's usually a good idea to listen to the warnings, because
often they'll give really insightful information about how
to improve your smart contracts. So to summarize, if it's red,
it's broken. If it's yellow, you might want to check it out. But
it won't stop you from continuing to code. So one thing th
at you'll notice here is that
we have this memory keyword. And you'll notice if you try to
delete it from our function here, you try to compile, you
actually run into an error data location must be memory or call
data for parameter and function. Now there are actually six
places you can store data in solidity you have the stack,
memory, storage, call data, code and logs. We're not going to go
over these right now. But we are going to focus on three of the
big ones, or three of the important ones
for this section,
which are called data, memory, and storage. So for this
section, we're going to talk about call data memory and
storage. And this is a little bit advanced. So if you don't
totally grasp it the first time, that's totally okay. Please
continue. Even if it's not crystal clear what's going on
here, call data and memory mean that the variable is only going
to exist temporarily. So this name variable only exists
temporarily during the transaction that this Add Person
function is cal
led storage variables exist, even outside of
just the function executing. Even though we didn't specify it
up above, our favorite number is automatically cast to be a
storage variable. Since it's not explicitly defined in one of
these functions. Since we don't need this name variable anymore
after this function runs, we can keep it as memory, or we could
keep it as called data, you can have a parameter as called data
if you don't end up modifying the name. For example, we
couldn't reassign name
to equal cat here, if we compile we run
into an error. Type literal string cat is not implicitly
convertible to expect the type string called data. However, if
we have this as memory, and we compile and save it, that error
goes away call data is temporary variables that can't be
modified. Memory is temporary variables that can be modified.
And storage is permanent variables that can be modified.
Now even though I just said there's actually six places
where we can access and store information, we
cannot say a
variable is stack code or logs, we can only say memory storage
are called data, you will learn why in a much later section.
Now, this is a bit of an oversimplification of this. But
that's essentially what's going on. The next question you might
have is, well, why do I need to say memory here, but I don't
need to say memory here. Well, let's go ahead and put memory
here and hit CTRL S or compile. And let's see what happens we
get from solidity data location can only be specified for
an
array, struct or mapping types. A memory was given arrays
structs and mappings are considered special types and
solidity. solidity automatically knows where are you in 256 is
going to be slowly knows that for this function, a un 256 is
going to live just in memory. However, it's not sure what a
string is going to be. Strings are actually kind of
complicated. Behind the scenes, a string is actually an array of
bytes. And since a string is an array, we need to add this
memory bit to it because
we need to tell solidity, the data
location of arrays structs, or mappings and a string is
secretly an array. So that's why we need to tell it it's in
memory. You'll notice we can't add the storage keyword here
slowly also knows that since this is a function, this name
variable isn't actually getting stored anywhere. So it says Hey,
you can't have that you need to have it beat memory or called
data and those are the only two that it accepts So this is what
we want our function to look like here
. So the summary of
this is struct mappings and arrays need to be given this
memory or called Data keyword when adding them as a parameter
to different functions. We'll learn more about storage memory
and call data in later sessions. Now, this list is great, but what if we know
someone's name, but we don't know their favorite number?
Well, what we could do is we could look through the whole
array looking for that person. For example, in our contract, we
can say, Okay, I'm looking for John. Okay,
let's start with
zero. No, okay, that's Patrick. Okay, let's go to one. Okay.
That's John. Oh, great. His favorite number 16? Well, this
was really easy, because we only had two people. But what if we
had hundreds of people in this array? Well, we'd keep have to
iterating all the way up to the index that that person was in,
it's obviously really inefficient. What's another way
to store this information so that it's much easier and
quicker to access? Well, another data structure that we can use
is something called a mapping, you can think of a mapping as
sort of like a dictionary, it's a set of keys, which each key
returning a certain value associated with that key. And we
create a mapping variable the exact same way we create all of
our other variables. This is going to be a type mapping of
string to you and tivity sex, this is going to be our type of
visibility keyword is going to be public. And we'll call it
name to favorite number. Now we have a dictionary where every
single name i
s going to map to a specific number. So let's add
some capability to our Add Person function. So we are going
to add our people to our array. But let's also add them to our
mapping here, what we'll do is we'll say name to favorite
number. App key name is going to equal to favorite number. So let's go
ahead, compile this. We'll go to our deploy screen,
we'll deploy this click, we have a new button named a favorite
number. If I type in Patrick, nothing shows up. By typing
Patrick, you'll see I get
zero response. By typing John, I also
get a zero response. If I type in Becca, I also get a with zero
response. When you create a mapping, you initialize
everything to its null value, every single possible string on
the planet right now is initialized to having a favorite
number of zero. So if we want to change that, we'll have to go in
and manually add that. So let's go ahead and add a person to our
mapping here. So we'll add Patrick. And we'll say my
favorite number is seven. And looks like t
hat transaction did
go through also add, Becca, and we'll say her favorite number is
13. Let John and we'll say his favorite number is 16. Now, if I
look up, Patrick, I'll immediately get back what
Patrick's favorite number is, I get seven back. If we look up,
John, we immediately get back 16. Back up, we may only get
back 13. And we also can see them in our array. Because we
kept in this people that push bit zero, we see Patrick's
there. At one, we see Becca. And at two, we see John, in our
map
ping, we're saying the string name is being mapped to the UN
256 favorite number. And a lot of my variables, I like to make
them explicitly named like that. So this is name to favorite number. So now we're in a space where let's
say that we really like our simple storage contract. Right
now we have a favorite number, a global variable that we can save
a favorite number to with our store function, we have a
mapping of name to favorite numbers, and we have an array of
a new type that we created ca
lled people, we can add to
both the array and to the mapping. Using this Add Person
function that we've created, we're able to save multiple
people's favorite numbers as well as kind of a global
favorite number as well. Let's say we really love this
contract, and we're ready to send it to a test net to have
other people interact with it. Now in future sections, you'll
hear me say that you shouldn't do this until you write tests
until you do some really simple auditing. But for now, let's go
ahea
d and learn how to actually deploy this to a test net or to
a real network. Now remember, test nets are run out of the
goodness of people's hearts. So if it's a little bit funky, or
maybe doesn't work exactly as we show here, that's okay. As long
as it works with the JavaScript VM, you'll be all set but it is
good practice to learn how to deploy these to a real test net.
Let's go ahead and do that. Our contract is here. Simple Storage
dot soul. It's compiled, compiling is passing we get this
lit
tle green checkmark here we go to the deploy section. Let's
go ahead and do Get this. And now we're going to change the
environment. So we were working with a JavaScript VM or kind of
this fake simulated environment, we want to now move to either
injected web three, or web three provider. If you hover over
injected web three, there's this really, really small text here.
But this basically means we're going to inject our meta mask or
our web three wallet into our browser to use similar to what
we
did with the faucet, we'll pick our account we want to use.
So I'm gonna go ahead and pick account one. And now we actually
see our account in the Account section of remix, injected web
three means we're using our meta mask or whatever web three
wallet, web three provider is, when we a little bit more
manually choose an endpoint. And we're not going to go over this
right now. But as we get later into the course, you'll
understand what this means. So we're picking injected web
three, whatever ne
twork are injected web three, or in this
case, our wallet is connected to is going to be the network that
we deploy to. So for this section, we're going to be
deploying to Rinkeby. But again, depending on whatever the
recommended test net and the recommended faucet is, that will
dictate which test net you should actually deploy to. For
us, it's going to be Rigby. To deploy to a test that remember,
we're going to need gas, so we're going to need some tested
Etherium. Or if you're deploying to a m
ain net main Aetherium,
which you shouldn't be come to the top of the GitHub repo to
make sure you have the most updated faucet, the other place
you can go is link token contracts page in the chain link
documentation and scroll down the ranking. And you can see
test that link available here, test that eath available here.
So this is the other location, you can always look to find the
most up to date faucets, and both of them point right back
here. So now that we're working with injected web thre
e, we can
just go through the exact same steps to deploy to a test net as
to deploy to a virtual machine. And remember, if you run out of
gas to deploy this, be sure to check back to the faucets to
actually deploy this. So we're going to do the same thing,
we're gonna go ahead and hit Deploy. But this time Metamask
is going to pop up and ask us if we want to actually deploy this,
this is the exact same as what we saw with the blockchain
example, where we sign transactions, we are signing and
sen
ding this transaction, the data of this transaction is this
massive, massive data thing here, which represents the
contract that we just created, we can see all the payment
information for this transaction for deploying this contract, we
see it's going to cost around this much Aetherium to deploy.
But again, we're on the Rinkeby test network. So this is going
to be fake Aetherium. We're gonna go ahead, hit Confirm. And
if you pop up a little console, and remix, you'll see that after
a slight del
ay, it'll actually say have this green checkmark
that it's confirmed that it went actually went through, we can go
ahead right click, open a new tab, and view this on ether
scan. And after a slight delay, we'll actually be able to see
the transaction details here, exactly the same as our
transaction details for sending Aetherium we have a hash, we
have a status, we have blocked block confirmations, we have
timestamp from which is going to be us two, which is going to be
the average of the contra
ct that we just created. We didn't send
any value with this. So this is going to be zero ether. And then
of course, we see the transaction fee, and as well as
the gas price. Because again, deploying a contract to the
blockchain is modifying the state of the blockchain. So we
have to pay gas, and we can see all the different pieces here.
As we can see, gas limit and gas usage is much higher than just
sending Aetherium, since we are putting a lot of data on chain
and adding a lot of computation. S
o this number is much higher
than the 21,000 number of just sending Aetherium. Now, if we
come back to our remix and scroll down, we're able to see
our simple storage contract at this address. If we hit this
copy button, and we go to the rink be ether scan, we paste it
in the search bar, we will get the contract that we just
deployed. And we see this first transaction is going to be the
contract creation transaction. So this contract that we just
created one transaction, which is contract create
d. So now that
we have this contract created, we have all the exact same
functions that we saw when working with the JavaScript
virtual machine, or the our fake environment, or our super fake
environment. Now we can do all the exact same things that we
did with the JavaScript VM, but on a real test network. So
you'll see if I hit retrieve Metamask doesn't pop up. Because
again, this is a blue view function. If we look people at
zero, this is also a view function and nothing pops up,
named a favo
rite number should be blank. So if I type in
Patrick now, absolutely nothing happens, right? I get I get zero
returned because mappings initialize every single key with
a blank or a null value, which for you 256 is zero. Now, we can
go ahead and store a favorite number store When your favorite
number is going to modify the blockchain, so our meta mask
should pop up for us to confirm the transaction and sign that
transaction to modify the blockchain state. So I'm going
to store my favorite number
of 16, we'll hit store, meta mask
will pop up, and we're going to go ahead and actually confirm
this didn't confirm is equivalent to US signing this
transaction and sending it to the blockchain to modify the
state. So we're gonna go ahead and confirm this, we should be
able to view this on ether scan. And again, it might take a
little bit for it to actually index or actually start working.
So please be patient with these test nets. And again, this is
why when building your applications, you wan
t the test
net piece to absolutely try to be your last step, because you
have to wait a really long time. And it puts a burden on these
people running these tests into running it out of the goodness
of their heart. So please try to make this the last step of your
actual building process. For us learning right here, it's okay.
And after a slight delay, once we hit refresh, it looks like
it's indexing on ether scan, the ether scan website is still
figuring out where the transaction is going to rem
ix,
it looks like on the blockchain, this has actually already gone
through. So now if we hit retrieve, we do indeed see, our
favorite number is 16. Of course, these two are still
going to be blank. And it looks like that transaction has gone
through and ether scan has indexed. So now let's go ahead
and add a person will add Patrick, and my favorite number
is going to be 16. We'll go ahead and add person. Again,
since these are orange transactions gonna pop up
because we're modifying the blockch
ain state, we'll go ahead
and hit Confirm. And we're going to be a little bit patient here
and wait for this transaction to go through. And we should see
this update. And this update. Now if I hit named a favorite
number of Patrick, I get 16. And if I hit people of zero, I get
favorite number 16. And the name is Patrick. Awesome. So you've
actually successfully deployed a contract to an actual test net,
and actually seen on ether scan what these transactions look
like, you should be incredibly p
roud of yourself, be sure to
give yourself a high five pat on the back, send a tweet saying
exactly how excited you are. But make sure to celebrate these
little wins, celebrating these little wins will give you the
motivation to keep going and really excite you for learning
each new thing. So huge. Congratulations. If you got this
far, you've deployed your first contract to a test net,
congratulations. Now if you want to see what it looks like to
deploy to a different network, all you need to do
in your
Metamask is switch to a different test net. See, if we
switch to COVID remix automatically updates and says,
ah, injected web three is now the COVID test network. We could
switch again maybe to Grilley, we say Ah, the injected web
three is now at the Grilley. This is the test net that we'd
be deploying to of course, we need actual test net Aetherium
to do any deploying, so we wouldn't be able to here. And if
we go ahead and hit Deploy right now Metamask pops up. But we get
this little r
ed thing saying insufficient funds. Of course,
later on, we'll learn how to add new networks like polygon like
avalanche like phantom into our Metamask. So we can deploy from
any one of them as well. Now, I mentioned this term
before, but all this code that we wrote, when we hit this
compile button, it compiles it down to the EVM or the Ethereum
virtual machine. Don't worry too much about what this means. EVM
is a standard of how to deploy smart contracts to Aetherium
like blockchains. And any b
lockchain that implements a
type of EVM. You can deploy solidity code to some examples
of EVM compatible blockchains are going to be avalanche,
Phantom, and polygon. Since these are EVM compatible, this
means we can write our solidity code, and deploy to these
blockchains, which again, I'll show you later on how to add
these new networks into your Metamask. And then how to deploy
them. Let's do a quick recap of our first smart contract. And
then you should actually take a break, maybe get some i
ce cream
or coffee because you absolutely deserve it. Congratulations. The
first thing you always need to do in your smart contracts is
tell solidity, what version of solidity that you're going to be
using. And additionally, you want to add an spdx license
identifier, then you have to create your contract object and
name your contract. The contract in solidity is similar to a
class in other programming languages, and everything inside
the squiggly brackets is a part of that contract. There are m
any
different types in solidity like unsigned integer, 256, Boolean
string, bytes, 32, etc. If we want to create a new type, we
can create what's called a struct in solidity. You can
create arrays or lists in solidity, you can create
dictionaries, or what's called mappings in solidity or hash
tables, which when you give it a key, it'll spit out the value
that that key represents. We can create functions in solidity
that modify the state of the blockchain. We can also create
functions in solidity
that don't modify the state of the
blockchain view in pure functions, don't modify by the
state of a blockchain, we also can specify different data
locations in our functions, called data and memory mean that
that data is only temporary and will only exist for the duration
of the function. Storage variables are permanent and stay
there forever. function parameters can't be stored
variables because they're only going to exist for the duration
of the function. All the solidity code that we work w
ith,
when we hit Compile, it actually compiles down to this Aetherium
virtual machine specifications. We'll learn more about those
specifications later. And last, but not least, another huge
congratulations on your first contract here. Awesome. All right, let's get started on
our lesson three, remember, everything is in the GitHub
repository. And we can scroll down, hit Lesson three and see
all the code here. I'm building up this repo as I film. So
underneath this lesson three is going to be a l
ot more
information than just the code here, all of our code samples
end with dash f f, c, which means dash Free Code Camp. So if
you see a GitHub repo that ends with dash F, F, C, know that
that repository is associated with this course, I'm going to
do a quick high level walkthrough of what we're going
to be building in this lesson. So you don't need to code right
now just sit back watch and enjoy. In this lesson, we're
actually going to expand to having three different
contracts. Let's say we
want to be able to deploy simple storage
contracts from a contract itself. Yes, contracts can
indeed deploy contracts, we are going to create a contract
called storage factory dot sole, that's going to be able to
deploy and interact with other contracts itself. So what we
could do is we could go deploy this to a JavaScript VM, we're
going to choose storage factory, and we're gonna go ahead and hit
Deploy. In our contract down below, we have a number of
different functions. Our top function is t
his function called
crate simple storage contract, which we can click and it'll
actually create a simple storage contract for us, then we can go
ahead and interact with it at an IG zero will save a favorite
number of one. Now, if we hit SF get zero, we get one back. And we can see the address of the
simple storage contract that we just deployed. Additionally,
we're going to learn about a number of solidity features,
such as importing inheritance, and so much more. So let's go
ahead and jump in.
And remember all the code is available here
from the GitHub repository. So be sure to refer back to these
contracts if you get lost. So here we are back in remix, and we have our
simple storage dot soul. If you skipped over the last section,
be sure to go to the full blockchain solidity course, Jas.
And scroll down to lesson two, welcome to remix and grab this
code. Go to simple storage outsole. And copy paste this
code into remix. Because this is where we're going to be starting
from, we have t
his simple storage contract, which is
great. It allows us to store a favorite number. And it allows
us to store favorite numbers across different people in both
mappings and arrays. But let's say we want to get even more
advanced with this, we actually can have a contract actually
deploy other contracts for us. And then go ahead and interact
with those contracts from other contracts, contracts interacting
with each other is an essential part of working with solidity
and working with smart contra
cts. The ability for
contracts to seamlessly interact with each other is what's known
as composability. smart contracts are composable,
because they can easily interact with each other. This is
especially awesome when it comes to things like defy where you
can have really complex financial products interact with
each other incredibly easily, since all their code is
available on chain. So we're going to learn how to do that.
So let's keep our simple storage contract exactly the way it is,
we're g
oing to create a new contract called storage factory.
So we're going to hit the New File button and type in storage,
factory dot salt. And let's close this off for now. So let's
go ahead and get this contract setup from what we learned
before. First thing we're going to want to do is the spdx
license identifier, which we're going to do MIT. And then the
next thing we're always going to need is our solidity version. So we'll do pragma solidity and we could do zero point 8.7.
But for this one, let
's do zero point 8.0 And then just add the
carrot, meaning any version of 0.8 point something will work.
And then let's add our contract name, which is going to be
storage factory. Now hit command S or ctrl S or go to the compile
tab and hit compile, and boom, we have our regular setup here.
Now we want to create a function that can actually deploy our
simple storage contract. So we'll create a function called
function. create simple storage contract, we'll have it be
public, so anybody can call
it will have to deploy a simple
storage contract and save it to a global variable. But before we
can do it How can our storage factory contract know what our
simple storage contract looks like? In order to deploy it, if
our storage factory contract is going to deploy simple storage,
it's going to need to know code of simple storage. One way we
can do this is we can actually go to our simple storage dot
soul and copy everything underneath pragma, solidity and
down and paste it into our storage f
actory dot soul
underneath our pragma, solidity. If we go ahead and compile and
save this, it actually works our storage factory, that soul
contract actually now has two contracts in it. It has the
simple storage contract, and it has the storage factory contract
in it, you actually go to the deploy tab and scroll down to
deploy while you're on the storage factory dot soul, not
the simple storage that's all on storage factory, you can see
that you can actually choose which one of these contracts
to
deploy, a single file of solidity can hold multiple
different contracts. Now that we have our simple storage, that's
all in our storage factory, we can actually go ahead and create
this function to deploy a simple storage console, we're going to
create a global variable the same way that we would create
any other global variable, we'll do the type, which is going to
be type simple storage contract, we'll give it a visibility of
public. And we'll give it a variable name. Type simple
storage co
ntract going to be public, the name of the variable
is going to be simple storage. Now in our function, create
simple storage contract, we're gonna say simple storage equals new, simple storage. This new keyword is how solidity
knows Ah, okay, we're going to deploy a new simple storage
contract. So we go ahead and compile this, we'll go to the
deploy tab, make sure we're on the JavaScript VM, we'll scroll
down to the contract. And we'll choose storage factory. And
remember, you need to have stor
age factory dot soul
selected. In order for that to show up storage factory, we'll
go ahead and hit Deploy. And now we see our storage factory
contract has two buttons. One is create simple storage, and the
other one is going to be a view of our simple storage contract.
If we click it right now, it's going to show us that it's
currently at address zero, because it gets initialized to
being blank. It's saying there is no simple storage contract
currently deployed. Now if we pull up our console an
d click
create simple storage, we see we created a new function call
storage factory dot create simple storage contract. And in
doing so we called this function which created and deployed a new
simple storage contract, we can now see what address the simple
storage contract is out by clicking the simple storage
button and we see the address associated with it. So now we
know how a contract can actually deploy another contract. But the
thing is, having this massive chunk of code above our storage
factory is a little bit redundant, especially since we
have our other file called Simple Storage dot soul. And
let's say we have a contract that has got a ton of other
contracts in it. Always copy pasting all these contracts is
going to be a lot of work. So instead, what we can do is use
what's called an import. So let's go ahead and delete our
contracts simple storage. And now we're just going to type
import dot slash simple storage dot soul. This import dot slash
simple storage dot Seoul is t
he exact same as our copy pasted
version of simple storage. That's all. It takes the path of
another file, it takes the path package or GitHub, which we'll
get to in a minute of another file and says, Okay, we're going
to paste that contract into the top of this contract here, we
actually see we go back to compile, we go to deploy, let's
delete our old contract, we can actually see storage factory dot
soul again, we can deploy it, click the drop down. And once
again, we can run those functions e
xactly the same.
importing our contracts like this is much nicer than always
copy pasting the code. This way, if we want to change something
in simple storage, we have one canonical place to go ahead and
change it instead of having to change it in multiple different
places. Now additionally, you'll notice the pragma, solidity if
we have our contracts in two separate files, we actually can
have different versions of solidity right now our storage
factory has carrot zero point 8.0 Which means that
anything
within the 0.8 range of this contract is okay. But for simple
storage that sole, it says anything in the zero point 8.8
And above range is okay. So if we were to try to change the
compiler version 8.5 And then go ahead and compile, a remix is
going to automatically bump it up to a better version that is
compatible with both of them, in this case eight point 13. But if
we, for example, changed our solidity version of storage
factory to zero point 7.0 And then tried to compile them. We
a
ctually end up getting an issue parser error source file
requires a different compiler version. This is because our
storage factory is saying hey, anything in 0.7 means is okay.
However, our simple storage is saying anything in the zero
point 8.8 And above is okay. So those two versions are not
compatible. So what we need to do is we need to make sure our
versions of solidity are indeed compatible. So let's change the
version back recompile. And now we're looking good again. Now
since we have th
is create simple storage contract, every single
time we call it right now, it'll just replace whatever is
currently in our public simple storage variable. Let's go ahead
and update this so that we can actually keep a running list of
all of our deployed simple storage contracts. So instead of
having this be a single variable, we'll make this a
simple storage array or list public simple storage array.
Now, whenever we create a new simple storage contract, instead
of saving it like this, what we're
going to do is we're going
to save it as a memory variable by saying simple storage, simple
storage equals new simple storage. And we're going to add
this variable to our simple storage array. So the same way
we did it before, we're gonna do simple storage array dot push,
simple storage. So I should spell storage, right? Let's go
ahead and compile this looks good. We'll deploy the storage
factory deploy. Like here, we now have simple storage array
View button, we'll do create simple storage. No
w we can view
the simple storage contract zero. Right now there's nothing
at one. But if we create another simple storage contract, we can
see the new simple storage contract address at index one. All right, so this is great, we can now keep track of all of
our simple storage deployments. But how do we actually interact
with them, let's say we wanted to be able to call the store
function on all of our simple storage dot souls from our
storage factory, you can think of the storage factory as almo
st
like a manager of all of our simple storages. That's all
let's create a new function that can do exactly that. So we'll
create function, and we'll call it S F store, which is going to
stand for storage factory store. And it's going to take you in
256, simple storage index, and a UNT 256, underscore simple
storage number. There'll be a public auction as well. Now in order
for you to interact with any contract, you're always going to
need two things. And we're going to refer to this a lot. You'
re
always going to need the address of the contract, and the ABI of
the contract. The API stands for Application binary interface,
the API will tell our code exactly how it can interact with
the contract, we'll go deeper into API as we move on. But if
you go to your compile tab, you hit compile, and things are
actually compiling. You can scroll down, and you can see
compilation details. And you can see a whole bunch of information
on your different contracts. You can see the name of your
contrac
t, which for our simple storage contract is simple
storage, you can see a whole bunch of metadata, like the
compiler, the language output settings, all this other stuff.
You can see the exact bytecode and the opcodes, which we'll
talk about much later. But you can also see this API, this API
tells you all the different inputs and outputs and
everything you could do with this contract. For example, in
our simple storage, if we look at the zero with index of our
API, we have a function add person.
If we look at one, we
see our name to favorite number, we looked at two we can see our
people three retrieve for store, it tells us all these different
ways we can actually interact with our contract. And the
different functions that we can call, we know where addresses
are, because we're storing them in this array here, our simple
storage array, we can also get the API because we're importing
simple storage dot soul when you compile simple storage on soul.
As you saw on the compilation details
, whenever you compile
it, it comes prepackaged with the ABI, we automatically get
the ABI just by importing it like this. In the future, we'll
see other ways that we can actually get API's really
easily. So to call the store function on one of our
contracts, we're first going to need to get that contract
object. So what we can do is we can say simple storage variable
named simple storage. Variable Name simple storage of type
simple storage is going to be equal to a simple storage
object. And in
stead of doing new simple storage, like we did last
time, we're just going to put the address of this simple
storage object in here, which again, we can get from our
array. And in this function, we're passing the array index.
So we can say simple storage contract at address simple
storage array. At index simple Storage index. This bracket
notation here is how you access different elements of arrays. So
if we want the zeroeth element of our list here, simple storage
index would be zero, and we pa
ss it into this bit here, then
that'll give us the address of our simple storage contract,
which we pass into simple storage here. Since this is an
array of simple storage contracts, we can just access
that simple storage contract using the index. So we would
say, simple store, Edge array at index underscore simple storage
index. Now we're saving the contract object at index simple
storage index to our simple storage variable. Our array here
is keeping track of the addresses for us. And it
autom
atically comes with the ABI is here. If this was just an
array of addresses of the contract objects, instead, we
would have to wrap the address in a simple storage object like
this. But we'll get to that much later. So for now, all we have
to do is this and we now have a simple storage contract object.
Now that we have it, we can call our store function on the simple
storage contract. So we'll call simple storage dot store. And
we'll store the simple storage number to it. So this is perfect. And
if we were to
deploy this right now, though, we wouldn't be able to read the
store function. So let's create another function that can read
from the simple storage contract from the storage factory. So
we'll create a function called S F get, which stands for storage
factory get, it'll take a un 256 underscore simple storage index.
This will be a public view function, since we're just going
to be reading from our simple storage contract. And it's going
to return a UNT 256. And we'll say simple s
torage. Simple Storage. Equals when you use this same syntax
from up here to get the contract simple storage array at the
simple storage index. And then we're going to do return, simple storage.re retrieve to get that number that we just
stored up here. And I should spell retrieved correctly, so we
get no issues. Perfect. So now we'll compile, we'll deploy JAVA
script, we're working on a fake account, we're going to use our
storage factory, go ahead and delete all the contracts we have
so far. L
et's go ahead and deploy the drop down rate. Right
now if we do SF get at zero, we're going to get nothing.
Simple Storage address zero is going to be nothing. Let's
create a simple storage contract. Now it's simple
storage list, we get an address at zero. If we hit SF get right
now it didn't zero, we get zero. So let's store a value on this
contract here. So the index of that contract is zero. So we're
going to pass zero as a symbol storage index. And we're going
to save the number seven. So we
'll go ahead and do SF store.
And if we did this, right, this is going to store the value
seven into this contract. So if we do SF get of zero now, it
does indeed return seven. If we do SF get one we're going to get
nothing's going to happen. And we're actually going to get this
revert error here. So let's create another simple storage
contract. Now if we do SF get one, we get zero, because we're
gonna get that default value. Let's go ahead on the simple
storage contract and index one will store
the number 16 will
hit SF store. Now we'll do SF get one and we get 16. Feel free
to pause right now and play around with this so that you
really understand it. The quick recap is our storage factory
contract allows us to create simple storage contracts, it
then saves it to our simple storage array, which we can then
call different functions on, we can store values from our
storage factory contract. And then we can read values from our
storage factory contract for any of the simple storage cont
racts
that we've created. This is incredibly powerful, we can
additionally make these two functions even easier. We can
call the Retrieve function directly on this when we call
simple storage array. And then we have these brackets and add
the simple storage index. This returns a simple storage object.
So what we could do is we could actually delete this whole part and just do dot retrieve right here, and then delete this
line and say return and just have it be just like this. If
you go ahead and
save or hit Compile, you'll get the green
checkmark there. We're calling the Retrieve function on
whatever this is. And whatever this is, is a simple storage
object. So perfect. We can do the same thing up here by
deleting this part and just doing dot store, underscore
simple store edge number, we save it. And this will work
exactly the same. Awesome, we now have a simple storage
contract that can store variables in a storage factory
contract that can be almost like a manager of these simple
st
orage contracts and deploy and interact with them themselves.
This is fantastic. Now, let's say that we really liked the simple
storage contract, but it doesn't do everything that we want it to
do. Maybe we want it so that whenever we actually store value
doesn't store the favorite number, it stores the favorite
number plus five, for some reason you want a contract that
that everyone's favorite number is five numbers bigger than what
they think it is. But you really like everything else that thi
s
contract has to offer. Let's create this new contract. And
we'll call it extra storage. So we'll say extra storage dot
soul. And we'll create this new
contract, we're going to set it up the exact same way we
normally would spdx license identifier, gonna be MIT, we'll
give it pragma solidity. And we'll just do zero point 8.0 with the carrot. And
we'll say contract, extra storage, like so if you save or compile, you'll
get the green checkmark. So what can we do? Well, the first thing
we could do
is we could copy paste all this code back into
here, and then modify our extra storage contract as we see fit.
This seems a little bit redundant. And a lot of work
though. So what's another way we can actually get our extra
storage contract to be like our simple storage contract? Well,
this is where we can do something called inheritance, we
can have our extra storage contract, do what's called a
child contract of our simple storage contract. And we can
have extra storage, inherit all the funct
ionality of simple
storage with two lines of code. So first, in order for our extra
storage contract to know about simple storage, we once again
need to import it. So we'll say import dot slash, simple storage
dot soul. And we'll say our contract extra storage is simple
storage. And we save or compile. Now our extra storage contract
is going to be the exact same as simple storage. And it's going
to do what's called inherit all the functionality of simple
storage. And we can actually even see tha
t, let's go ahead
and make sure this is compiled. And we'll go and deploy this.
And now in our deployed contract, we can see we have
extra storage deployed with all the functions that simple
storage has, if you want a contract to inherit all the
functionality of another contract, you can just import it
and say your contract is that other contract. Now, we can add
additional functions to our extra storage contract. That
will include all the functionality of simple storage.
Now, let's say that we
inherit simple storage to extra storage.
However, one of the functions in simple storage we don't really
actually like. So if we go back to our simple storage contract,
our store function, all it does is take a favorite number, and
then assigns the global favorite number to whatever new number
that we give it in our extra storage. We want the store
function to do something different. We want it to add
five, to any number that we give it. How can we achieve this?
Well, we can do something called
overriding the functions. And
there are two keywords that we're going to use. Those are
virtual, and override. Right now, if I were to try to
implement a store function for extra storage, let's see what
happens. We'll say function store, you went to the six
favorite number. There'll be a public function. And let's say
instead of just storing favorite number, we'll say favorite
number equals favorite number, plus five. If we try to compile
this right now, we'll actually run into two different err
ors.
First one is going to say overriding function is missing,
override specifier. If the parent contract, which in our
case is simple storage has that same function, we need to tell
solidity that we're going to override this store function and
instead we're going to use this store function. But
additionally, we get this other error saying trying to override
non virtual function. Did you forget to add virtual in order
for a function to be overridable you need to add the virtual
keyword to the st
ore function. Now it can be overridable.
However, if we save and compile, we still have this issue
overriding function is missing override specifier. And then all
we need to do is add override to restore function. Now if we save
everything compiles correctly, let's go ahead and deploy this.
Let's delete our old contracts. JavaScript VM right account.
Great we're going to choose extra store Reg, let's go ahead
and deploy. And here's our extra storage contract. Right now if
we retrieve, we get zer
o. Previously, our store function
would store the exact number. However, if I were to store
five, it'll store five, plus five. So we should have 10
stored here. Let's go ahead call store looks like that one
through, and we'll hit retrieved now. And we do indeed see 10 is
in here. So this is how we do inheritance, and we override functions. And that's it. For
this section, you've just learned a
ton of incredibly powerful solidity for having multiple
files. Let's do a quick overview of what we lea
rned, we learned
that we can actually deploy contracts from other contracts
using the new keyword, we learned that we can actually
import other contracts into our contracts and into our code
using the Import keyword. And the import keyword is the same
as copying pasting that file to the location of the import line,
we learned that we can interact with other contracts. As long as
we have the ABI and address. We didn't learn too much about the
ABI. But we'll learn more later, we learned that if we
want to
create a child contract and inherit the functionality of
some other contract, we can do something called inheritance.
And the way to inherit functionality is using the is
keyword and saying our contract is some other contract. However,
if we want to change from the functionality of the parent
contract, we have to override that function. And additionally,
we have to set the function we want to override to virtual now
we can have our own store function, do whatever we want it
to do. That
is the end of this lesson. Once again, give
yourself a huge round of applause a pat on the back for
making it this far. And for finishing this section. You're
getting more and more advanced facility so quickly, so be sure
to celebrate the little wins by getting some ice cream, maybe
going for a walk or tweeting about or posting on Reddit.
Congratulations, you have completed this section. All right, everybody. Welcome
back. We are now headed into Lesson four remix Funmi. And of
course, all the co
de can be found on the GitHub repository
associated with this course. We're going to be working with
two contracts here, one of them is fundament outsole. And then
one of them is price converted outsole Funmi dot Sol is going
to be a contract that allows people to actually fund a
collective good. So people can send Aetherium send Aetherium,
or polygon or Avalanche or Phantom, or whatever blockchain
native token into this contract, and some owner of the contract
can then withdraw those funds and
do whatever they want. After
deploying this to a test net, we can see the list of functions
this contract has, this will have two red buttons, which are
used to notate to payable functions in fund in our fund
function. And in our withdrawal function, withdraw allows users
to withdraw the funding and fund allows users to send money to
the contract, what we can do is we can send some value along
with our transaction. When we call this fund function, then
what we can do is we can actually fund this
contract with
a certain amount of eath, or way by pasting some value into the
way value section. And then hitting fund, we will now have
sent money into our deployed contract. And we can see a list
of the funders and a mapping of those addresses and how much
they've actually sent into the contract, we can then withdraw
the funds out of the contract. With a special exception with
only the person who deployed this contract can actually
withdraw the funds back out. Once the funds are withdrawn,
th
e amount of all the funds is reset back to zero. Are you
excited? Well, you should be. And if you've finished the
section, you've completed most of the basics of solidity, and
you'll be ready to start making even more powerful smart
contracts, we'll be using channeling price feeds to
actually set the value of how much these people should be able
to fund in USD as opposed to just in terms of ether, we're
gonna go over a lot of advanced sections here. And I'll let you
know what parts might be a li
ttle bit harder to digest. So
you don't have to spend your entire time trying to figure out
exactly what's going on. Be sure to use the GitHub repo to your
advantage here and the discussions tab to stay
connected with other people taking this lesson. Alright,
let's jump in. So at this point, in remix, you'll have a couple
of contracts here, simple storage, storage, factory extra
storage, maybe you refreshed remix, and these have gone away.
And in the case, make sure that you just don't have any
of those
tabs open, we're going to create a new contract called funding.
So let's go ahead and start creating our funding contract.
Again, we want it to be able to get funds from users and
withdraw funds and set a minimum funding value in USD. This is
what we're going to get our contract to do. So first, let's
set it up spdx license I tend to fire MIT do pragma solidity do
caret zero point 8.8. And we'll do contract bunbee. Awesome. And
we'll compile, see if things look good. And they do. Great.
Let's keep going. So before we actually embark on creating all
of our functions here, let's just add the different functions
that we're going to implement. So we want a function fund for
people to actually send money to, we want a function withdraw,
or the owner of this contract to actually withdraw the funds that
different funders actually give us. And that's pretty much it.
These are the two main functions that we want this contract to
do, we will be implementing more functions to help facili
tate
these two functions. But let's get started by looking at fund.
let's comment out withdrawal for now. And let's just start with
fun. So we want anybody to be able to call this fun function.
So we'll make this public. So as we mentioned, we want to be able
to set a minimum font amount in USD. So there's a lot of things
to think about here. First thing we probably want to think about
is how do we send eath to this contract, whenever we create a
transaction on the on any of these EVM blockchain
s, there's
this value field that we can set value represents how much
Aetherium we're going to be sending with our transactions.
For example, when we transferred Aetherium. between our different
accounts, we were actually populating this value parameter
with different amounts of Aetherium. In fact, every single
transaction that we send, will have these fields, it'll have a
nonce or the transaction count the account the gas price, the
gas limit that we've seen on ether scan a to aka the address
t
hat the transaction is sent to a value which is going to be
this amount that we're talking about. They'll also have data
which is going to be what we send if we make a function call
or deploy contract, and then we'll have this v r s
components. We're not really going to go over these v r and s
because this is that cryptographic magic that's
happening when a transaction is signed, but just know that
that's it Now, for sending value, we can populate some of
these fields, the gas limit, for example
, was populated to 21,000
data is going to be empty. And then that two is going to be the
address of the transaction we want to send to, for a function
call, we can also still populate the way that we want to send. So
we can call a function and send a value at the same time in
remix has a little drop down here for way way Feeny and
ether, we're gonna ignore Feeny for now. But of course, we have
our way, great and ether. Again, we're one ether is worth this
much way. And this much way, the first
thing we need to do in
order to make a function payable with Aetherium, or any other
native blockchain currency is we need to mark the function as
payable. It's this payable keyword that makes our font
function red, as opposed to having it normal orange, just
like how our wallet can hold funds, contract addresses can
hold funds as well, since every time you deploy a contract, they
get a contract address, it's nearly the exact same as a
wallet address. So both wallets and contracts can hold nativ
e
blockchain token like Aetherium. And you'll see that when we
deploy this later on in the lesson, that actually will gain
a balance of Aetherium. Now that we have it payable, we can
access this value attribute by using one of the global keywords
in solidity with message dot value to get how much value
somebody is sending. You use message dot value in your
function. Now let's say we want to set our message add value to
a certain value of Aetherium. Let's say we wanted it to be
let's say we wante
d people to send at least one whole ether
with all the transactions. Or put another way, if they sent
Aetherium, they would need to send at least one Aetherium. How
would we implement that? Well, we could do something called
require, we would say we want to require the message dot value is
greater than one e 18. There's a couple of things to unpack here.
One, e 18 is equal to one times 10 raised to the 18th, which is
also equal to 112345 678-910-1234 5678. This
is the value in way of one Aetheri
um this much way is one
eath. So if we wanted the message dot value to be at least
one eath, or one polygon or whatever ranch etc, we would set
it like this require message on value is greater than one. This
require keyword is a checker it says hey, is message dot value
greater than one. If not, it's going to revert, it's going to
do what's called revert with an error message. And we can say
didn't send enough, we're gonna try deploying this on a
JavaScript VM, deploy Funmi, we'll hit Deploy, lo
ok at Funmi
we see this fun button is now red. If we call Funmi right now,
and we look at the console, you can see we actually get an error
here, call it again, we get an error, we get an error here, we
know that the air is going to be this didn't send enough. So what
we need to do is we need to send at least one ether with this fun
transaction, in order for this require statement to be
fulfilled. So back up in the value section, we can change
this value to one. So that's gonna be one ether, or
this much
way or this much way. Now we can hit fund oh actually needs to be
greater than one. So let's send to for example.
Now we'll scroll down. And now we'll hit fund. And we see that
that actually passes the require statement says if our first
section is false, then go ahead and revert with this error. What
is reverting? Revert can be a little bit confusing, so I
wouldn't let this section hold you back. If it's a little bit
confusing. Revert is when it undos any actions that happened
before
and send the remaining gas back. So what does that
actually look like? Well, let's say for example, we had a un
256. Public number. And in our fund function, we said number
equals five. If we were to go ahead and deploy this, let's
delete our old contract. deployed this new contract or
number right now is zero. But if we were to call our fund
function number gets set to five. However, if we call a fund
and this require isn't met, this transaction would revert an
undue setting number to five. So
let's go ahead and look at our
logs here. We'll keep value zero so that our fun function
reverts. We'll call fund will see that this transaction failed
because this require end up reverting and number is still
zero. So then the question becomes, did we actually spend
gas Yes, we spent gas to change number to five and then any
remaining gas we would get returned by this require for
example, if we had a ton of computation here, it On a
computation here, we would have need to send a ton of gas with
our fun function. But all the extra gas that we send, after
this require gets returned the original user after it gets
reverted right here, if reverts are a little bit confusing for
you here, don't worry too much about it, we'll go over it in
future modules. All you need to know right now is that when you
do a require statement, if this first section isn't met, the
transaction will be canceled, and any prior work will be
undone, and it'll send an error message. Cool. Let's delete this
number fo
r now. And we'll delete it from the global scope.
There's actually another way to do these reverts, which we'll go
over later in this contract. So what
we've done so far is great. However, we're checking message
dot value in terms of Aetherium, we're looking for one whole
Aetherium instead of $50, we want to check that message add
value is greater than some number like $50. Let's go ahead
and first set the minimum USD value we want people to send
along with the fund function, we can do that at t
he top of our
contract, we can say you went to 56 public minimum USD equals 50.
Now we have some place to check for minimum USD, we're going to
update this minimum USD to make it more gas efficient and a
little bit. Now that we've set our minimum USD, we want to be
able to require the message that value is greater than or let's
say greater than or equal to the minimum USD. But minimum USD is
in terms of USD and value is in terms of Aetherium. So how do we
convert Aetherium to USD, this is where
Oracle's and chainlink
are going to come into play. The USD value of Aetherium is
something that we've assigned outside of the blockchain to
Aetherium, or any other layer one currency or any other native
smart contract platform currency. So in order to get
this value that is outside the blockchain, we have to use a
decentralized Oracle network to get the price of one ether in
terms of USD. So before we can continue on here, let's learn a
little bit more about the architecture of these
decentrali
zed Oracle networks and the different solutions that
they have. So that we can create this Funmi contract in the most
advanced way possible, as we've talked about blockchains are
deterministic systems, which means that they themselves can't
actually interact with real world data and events. They
don't know what the value of an Aetherium is, they don't know
what random numbers are. They don't know if it's sunny
outside, they don't know the temperature, they don't know
who's president, they don't
know any of this information. These
blockchains also can't do any external computation. Maybe you
have some amazing artificial intelligence model that you want
to integrate with a smart contract. smart contracts by
themselves can't do anything with that. As we've mentioned,
this is because blockchains are deterministic by design. This is
so that all the nodes can reach consensus. If you start adding
variable data or random data, or values that returned from an API
call, different nodes could get
different results, and they
would never be able to reach a consensus. This is known as the
smart contract connectivity problem, or the Oracle problem.
And this is bad news, because we want our smart contracts to be
able to replace traditional agreements. And traditional
agreements need data and they need to interact with the real
world. So this is where chainlink and blockchain,
Oracle's come into place. A blockchain Oracle is going to be
any device that interacts with the often world to provid
e
external data or computation to smart contracts. However, the
whole story doesn't even in there. If we use a centralized
Oracle, we are reintroducing a point of failure. We've done all
this work to make our logic layer decentralized. But if we
get our data through a centralized node or through a
centralized API, we decide we want to make the API call
ourselves. We are reintroducing these trust assumptions that
we've worked so hard to get rid of, we're essentially ruining
the entire purpose of
building a smart contract. So we don't want
to get our data or do external computation through centralized
nodes. Those are bad news. chain link is the solution here. chain
link is a decentralized Oracle network for bringing data and
external computation into our smart contracts. As we mentioned
before, this gives rise to these hybrid smart contracts, which
combined on chain and off chain to make incredibly feature rich,
powerful applications. chain link is a modular, decentralized
Oracle networ
k that can be customized to deliver any data
or do any external computation that you like. So for example, a
lot of people say, Oh, I can just make an HTTPS call to some
API, and we'll be good to go. The blockchain nodes can't make
these HTTPS calls, because they wouldn't be able to reach
consensus. If they called the node at different times, or they
did something else. All the consensus would be broken. So
instead, we need a decentralized network of chain link Oracle's
to do this, and then in t
he transaction, this network of
nodes will work Turn the data to our smart contracts for us. Now
chainlink networks can be completely customized to bring
any data or any external computation that you want.
However, doing the customization can be a little bit extra work,
there are a ton of chainlink features that come out of the
box completely decentralized, ready to plug and play into your
smart contract applications. What are those features, the
first one is going to be channeling data feeds, a
nd
that's the one we're actually going to be using for
application here. Channeling data feeds currently at the time
of recording are powering over $50 billion. In the defy world,
the way they work is a network of chain link nodes gets data
from different exchanges and data providers and brings that
data through a network of decentralized chain like notes,
the chain link nodes use a median to figure out what the
actual price of the asset is, and then deliver that in a
single transaction to what'
s called a reference contract, a
price feed contract or a data contract on chain that other
smart contracts can use. And then those smart contracts use
that pricing information to power their defy application, we
can see an example. We can see an example at data dot chain dot
link. And you can change networks, you can change price
feeds, you can change a whole bunch of different information
to see some of those popular price feeds. Let's look at eath
USD for example. On eath USD, we can see this
whole network of
independent chain link node operators that are each getting
different answers for the price of eth USD, they're getting
aggregated by the network and then delivered on chain, we can
see how often they're updated. These ones are updated 4.5
deviation threshold or a few hour heartbeat, whichever one
hits. First, we can see when the last update was we can see the
number of Oracle responses etc, we can see the contract address
directly on chain, we can even look at the contract on
ether
scan, we can see some of the history, we can see all the
responses of the different Oracle's. And then at the
bottom, we can see the different users and sponsors, keeping this
network up. Similar to transaction gas, whenever a node
operator delivers data to a smart contract, the chain link
node operators are paid a little bit of Oracle gas in the chain
link token. Right now these users of the protocol are
sponsoring keeping these feeds up and are paying the Oracle gas
associated with deliv
ering this data on chain. Here's an
illustration of what the current model of these data feeds look
like a network of these chain link nodes, each reaches out and
gets the information about an asset and then signs the data
with their own private key in a single transaction, then, one
node will deliver all the data with all the different
signatures to a reference contract. If that node doesn't
deliver the data, another node will send it instead. Reputation
is incredibly important when your chain
link node operator if
you miss data updates, if you forget to send transactions,
you'll probably be quickly kicked off these networks and
have no chance of making any more money in the future. These
data feeds are used by some of the largest protocols in the
space, such as synthetics, sushi swap compound, and Avi, with
several billion dollars each, we can take a look at an example
over at Doc's dot chain dot link work with EVM contracts, we're
going to hit EVM chains, scroll down to data feeds.
We'll scroll
down to the solidity section. And we can see an example of an
entire contract that uses and reads from one of these trending
price feeds. We can even open this up and remix and work with
it and remix. It looks like this example is reading from a price
feed on COVID. The reason we're actually going to use a test net
to see this work is that there's a set of chain link nodes
monitoring the test network. Just to show you exactly how
this works out. Once we get deeper into the course, w
e'll
show you how to actually run tests and work with chain link
nodes without actually being on a test net, which will make your
development much faster. But I highly recommend walking through
this section along with me so that you can see firsthand how
this actually works. So let's go ahead and faucets dot chain dot
link slash COVID. We're going to switch to the COVID network,
we're going to get some COVID eath. But remember, look at the
network flag and use whatever network is in the document
ation.
So to get some COVID, we're going to come to the faucet,
we're going to turn off test link, we'll just stay with eath
I'm not a robot, and then send request. Once our
COVID Aetherium has reached our wallet, we can go ahead and
close we can take a look in our wallet and see that we do indeed
have 0.1 eath on Kelvin. Now let's go back to our remix,
we'll compile this contract, go and deploy this on injected web
three. And again, the reason we're going to use injected web
three instead of Ja
vaScript VM is that there's no network of
chain link nodes watching our little fake JavaScript VM. There
are a network of chain link nodes watching the test net. So
we'll scroll down. We'll switch contract to the price consumer V
three, and we'll hit Deploy and a mass will pop up and after a
brief delay, we can see our price feed consumer down here
and we can hit Get the latest price which shows us the latest
price of Aetherium in terms of USD you may be wondering why the
number looks so weird.
That seems like a really large number
for the price of Aetherium in terms of USD, and this is
because decimals don't actually work so well in solidity, and
we'll get to that in a little bit. There's a decimals flag
associated with this price feed address, that tells us how many
decimals to include with this price. It's also in the
documentation. However, I know that this one has eight
decimals. So this is saying the value of Aetherium right now is
$3,262. It may of course be different when you g
o ahead and
try this. Now there's a number of things that happened in this
contract that I'll explain in our Funmi example. But if you
want to take a look now and see if you can figure out what's
going on, I recommend you do so. Price feeds are one of the most
powerful out of the box decentralized features, you can
use your smart contract to level them up, especially for
decentralized finance. If you're looking for different addresses
of different price feeds, you can check the contract addresse
s
section of the documentation, choose the network that you
want, and then scroll down and look some of the different
addresses of the different price feeds. For example, this address
will give you the price of one inch token in terms of
Aetherium. This address will give you the price of the Apple
stock in terms of USD, and so on and so forth. The next
decentralized application right out of the box is going to be
channeling VRF or channeling verifiable random dysfunction.
Once we do our lottery
example a little bit later, we'll talk
about how randomness can be manipulated in blockchain.
blockchains are deterministic systems, which by definition
means that they can't have randomness. If you can determine
what a random number is, it's not really random anymore, is
it? So we need to wait to get a provably random number by
looking outside of the blockchain and Oracle's are
perfectly positioned to do exactly that. chainlink
verifiable randomness function is a way to get provably a
random nu
mber into our smart contract to guarantee fairness
and guarantee randomness of applications. Many protocols
like pool together x infinity, ether cards, avocado cheese and
more use channeling VRF for lotteries, randomizing NF T's
for gaming and for more, we're going to do an example of
channeling VRF in a later section. Once we get to the
lottery section, if you want to see if you can play with the
randomness yourself right now, I recommend you go into Doc's
chain link EVM chains, and scroll down
to get a random
number. And this will teach you how to get a provably random
number into your applications. The next decentralized out of
the box feature of chain link is chain like keepers, which is
decentralized event driven execution. As we've seen, in
order to kick off some type of transaction, somebody needs to
spend the gas and somebody needs to sit down and hit the go
button or hit the transact button or hit the sun. But this
is obviously a centralized vector. If you have a
decentralized
application that needs to run at specific times,
or after specific events are triggered. Channeling keepers
are the solution to this channeling keepers are chain
link nodes that listen to a registration contract for
different events that you specify to fire. Maybe you say
every 10 minutes, you want to do something or once a week do
something or if the price of some acid hits some number, or
maybe a liquidity pool is at a certain level, whatever event
that you want to code, you absolutely can th
e chain link
nodes constantly listen for these triggers to happen and
check the different contracts for these triggers. Once a
trigger returns true, the chain link nodes will then perform
whatever action that you tell the chain link nodes to do,
we're also not going to go over the chain link keepers examples
right now, because we're going to get to them in a later
module. However, if you want to try them out, go to Doc's
touching that link slash Aetherium. Going and go to
making compatible contr
acts and feel free to read the
documentation. Try it out yourself. The last out of the
box feature of chain link is the most customizable, but also the
hardest to get correct end to end reliability is the ultimate
promise of our smart contracts. And we want and need them to be
able to do anything, we want to be able to take any input and
get any output making HTTP GET HTTP POST request is an easy way
to customize our chain link nodes to be able to do anything.
Remember how we talked about making
API calls that blockchain
nodes themselves can do that? Well, chain link nodes can do
that chain link nodes can make direct requests to any API that
you specify. In order to do this, you both have to choose
the chain link node and the URL slash data to send the request
to this is a little bit trickier than chain link VRF keepers or
price feeds because you then have to be responsible for
creating the chain link network that gets data from many
different chain link nodes and many different data p
roviders. But let's look at an example in
remix anyways. For this section, feel free to just watch it since
we are working with a test net here. And test nets, as we've
seen, can take a little bit of time. As long as you're familiar
with what this process looks like. That's good enough. You
don't actually have to try it if you don't want to. So we'll open
up and remix will read through. It looks like this example is on
the COVID network. So we'll go ahead and compile API consumer.
We're gonna go
ahead and deploy on the injected web three, we're
going to make sure that we're back on the COVID test network.
We're going to scroll down. And we're going to change the
contract to the API consumer. And we're going to go ahead and
hit Deploy, we're going to deploy this contract to the
COVID. Test net. And now we can call this function called
Request volume data to actually make an API call. Now, like I
mentioned before, whenever we request data from a chain link
node, we have to pay a little b
it of Oracle gas, or link
token, in order to pay some link token, we're going to need to
have link token in our API consumer contract. This is
what's known as the basic request and receive model to get
link token, we go back to our faucet, and this time, we'll
select 10 test link for our contract. Let's go ahead and
verify that we're human. And we'll hit Send Request. This
time, instead of sending us Aetherium. They're sending us 10
test link, which is what's known as an ERC 20 token. Or more
ac
curately, in ERC 677 We'll get to understanding that a little
bit later, we can see the asset in our Metamask. By importing
the token, in order to get the token, we're going to come back
to the documentation. And we're going to look up link token
contracts like that, we're going to go to the network that we
just got the tokens on, which for us was COVID. We're going to
copy this address, we're gonna go to Metamask, hit import
tokens, paste that address, and hit add custom token, and then
import
tokens. And now we can see in our account one, we both
have Aetherium and 10. Link, now that we have our link or Oracle
gas, we're going to send it to our API consumer, we're going to
copy the address of the API consumer, open up our meta mask,
we're gonna hit send, paste the address of our contract, switch
the asset to link. For now we'll just send 0.2 link. We'll hit
next, next. And we'll hit Confirm. And we'll wait for this
transaction to go through. I chose 0.2 Link, because in this
contract
, there's a fee character, which tells us how
much making an API call for this is going to cost. This one is
actually 0.1 link, I send 0.2. Just in case, we want to make
that API call twice. Everything that's going on in this function
will explain in a little bit later section. But for now, I
just want to show you what it looks like to do is, once we
send the link to our contract, we can first check to see what
the volume is volume is zero, we want to get the volume of the
last 24 hours of Aethe
rium asset, we're going to be calling
this API which has a ton of raw data, including one in specific
called volume over the last 24 hours, which can be this number
right here. Say we wanted to get this into our contract from this
API, we're going to make an HTTP GET call to this API. And what's
going to happen is we're going to make the request in one
transaction. And in a second transaction, the chain link node
is gonna return the value and store it in this volume variable
in the global scope.
So let's go ahead and hit request volume
data Metamask gonna pop up, we're going to go ahead and hit
Confirm. And you'll notice right away volume doesn't update. This
is again, because we actually need to wait two transactions,
we're sending a transaction for the request. And then in a
second transaction, the chain link node is actually going to
respond. And after a slight delay, the chain link node has
indeed responded with the result of making that API call back to
our contract. We'll go over
this process a little bit more in
depth in later sections. The reason that I wanted to show you
specifically the API calls, is because we're going to show you
a real life example of how to use chain link VRF and chain the
keepers in a later lesson. Now I know we've already gone
over a ton. So let's do a quick review. In order to send
Aetherium or whatever native blockchain token with a function
need to mark it as payable. If you need something in your
contract to happen, and you want the whole
transaction to fail.
If that doesn't happen, you can use a require statement. To get
the Aetherium or native blockchain token value of a
transaction, you can use the global keyword message dot
value. chain link is a technology for getting external
data and doing external computation in a decentralized
context for our smart contracts. Channeling data feeds or price
feeds are ways to read pricing information or other pieces of
data from the real world that's already aggregated and
decentralized fo
r us, channeling VRF is a way to get provably
random numbers from the real world into our smart contracts.
Channeling keepers are a way to do decentralized event driven
computation. We can set some trigger say if this trigger hits
do something and we get to define what the trigger is and
what to do something is channeling any API's is the
ultimate customization of channeling nodes and allows us
to connect to anything on the planet. To make this one
production ready. We have to do the most work b
ecause it doesn't
come already with a decentralized Oracle network,
like chaining the keepers and price feeds. We'll learn more
about these channeling services as we continue in this course.
Now in order for us to figure out if our message dot value is
actually greater than the minimum USD that we set, we
actually have to convert our message dot value from its layer
one slash Aetherium to the USD equivalent. So how are we
actually going to do that? Well, first, we're gonna need to get
the price
of Aetherium, or Phantom, or Avalanche or
whatever layer, one blockchain that we're working with. So
let's create a function to get that price to get that
conversion rate. So we'll do function, get price, and this is
going to be the function that we use to get the price of the room
in terms of USD, so we can convert our message dot value to
USD. And then we're also going to do a function called Get
conversion rate. These are both going to be public functions, so
that we can go ahead and call the
m and test them and do
whatever we want with them. So in order to get the price, we're
going to have to use one of these chain link data feeds to
get the pricing information. And we can look right here at this
contract to see what using one of these channeling price feeds
looks like. What we're actually doing when we're interacting
with this channeling price feed is we're actually reading from
one of these contracts, there's a contract out there called the
aggregator contract that has a function
called latest round
data, which returns a whole bunch of data. But namely, this
int price. And this in price is what we are interested in. Let's
look at our get price function and figure out how do we
actually call this since this is an instance of us interacting
with a contract outside of our project, we're going to need two
things. What are those two things, we're going to need the ABI of
the contract, and also the address of the contract. So the
address of the contract is going to be easy, w
e can get the
address of the contract from the contract address this section of
the chain link data feeds. Let's scroll on down to bank B. And we
can find the eath USD address on Rigby and we'll create this
contract so that it works on Rinkeby. So we're going to grab
this address, we're going to copy it. And we're going to move
back to our to our code here. And we're going to paste the
address here. So great, we have the address. Now we have the
address of the other contract that we don't want t
o interact.
Now, how do we get the ABI? Well, what we did before was
simple storage was we imported the entire contract into our
code here. That's something that we could do. But that's actually
a lot of code. So what's something that we could do
instead, remember, if we're looking at remix, and we look at
one of the contracts that we compiled before, the ABI is
really just kind of this list of the different functions and
interactions you can have with a contract. The ABI itself doesn't
actually
need to include any of the logic, it just needs to
include, hey, here are the different functions that you can
call, for example, in this contract, we can call fund, we
have get conversion rate, we have get price, they're not
implemented yet, but they will be eventually now there
technically is another way to interact with contracts without
the API. But for now, we're just going to ignore that. So how can
we get the API, there's a concept in solidity called an
interface. And let's look at an ex
ample of an interface. If we
go to github.com/smart, contract kits, chainlink, we can see a
number of different contracts in the chainlink repository, we can
go to contracts, src, V 0.8, interfaces, and we'll go to
aggregator v3 interface, dot soul. And if we look at the
solidity in here, we can see a whole bunch of function
declarations, but none of the logic is actually implemented in
this. This is what's known as an interface. If we compile this,
we'll actually get the ABI of a contract, beca
use it defines all
the different ways we can interact with the contract. It
doesn't actually say, what these functions do, which is fine,
though, because we don't need to know what the functions actually
do, those are going to be stored in the contract. So what we can
do is we can grab this interface from the code and paste it into
our remix. Now hold on, if you're following along, you
don't have to copy paste this with me, because I'm going to
show you an easier way in just a second. So for now
, feel free to
go ahead and just watch. But once we have this interface
aggregator v3 interface, we can now use this to make API calls.
So now we could say, aggregator v3 interface at this address.
And the combination of these two, give us that aggregator v3
contract with whatever code is here. If at this contract
address is aggregator v3 interfaces valid, we can do
something like dot version. Let's look at this interface. Is
there a version function? There sure is. So that means we can
call the
version function on this contract. So let's actually
go ahead and copy this into a different section. I'm going to
create a new function called Get version just to illustrate this.
I'm going to be public. It's going to be a view and it's
going to return the UN 256 and We're gonna split it up into two
steps here. We're gonna say aggregate tour, v3 interface,
price feed. So we're creating a variable of type aggregator v3
interface equals aggregator v3 interface at this address. And
then we're goi
ng to return price feed that version. Now I'm going
to go ahead and deploy this contract to Rigby just to show
you what this git version is going to return. But you don't
have to follow along here if you don't want because again, we're
working with the test net, you can just watch if you'd like for
this section, we're going to test a little bit more sparse.
Since we're going to be mainly using the test net, since we're
going to be working with an actual chain link Oracle
network, once you move o
ver to hard hat, and with JavaScript,
all this testing locally will be a lot easier and a lot faster,
you're more than welcome to go ahead and fiddle and try and
test a lot of this stuff as we go along. But just know that it
might take a little bit longer to do some of the testing on the
test net, let's delete that last funding
contract, we're going to deploy this one, we're going to scroll
up, we're going to switch to injected web three, we're going
to switch from COVID to rink B. And the reaso
n we want to make
sure we're on rink beam is because this address is specific
to rink B, the contract that we're looking to interact with
might not be at this address on every single chain, we want to
make sure we're on the rink B chain for this, because of some
other contract is there on the different chain, this version
function obviously won't exist, and this function could error.
So let's go ahead, we're gonna find me, we're going to deploy
this to the rink V chain. Again, you don't have to
follow along
with me here, you can just watch. And once that contract
has been deployed, we now have a view function called Get
version. And we can see it's returning the variable for
showing us that this is the fourth version of a price feed.
So this is a really easy way for us to interact with contracts
that exist. Outside of our project, we use one of these
interfaces, which can get compiled down to the API, and
then combine that ABI with the address to call a function. As
we work with these
interfaces more and more, they'll start to
make more and more sense. So if it's a little confusing to you
right now, don't get discouraged, the more you work
with it, the easier it will become. Now, though, now that we
know how to call these functions in here, we can start working
with this interface. However, as you know, if we have a whole
bunch of interfaces, we're gonna have to stick a whole bunch of
interfaces at the top of our code, which looks pretty ugly.
What's a better way for us to do
this? Well, before we used
import, right, we imported from simple storage. That's all for
this one, what we could do is we could import from an array gay
Tore v3 interface. That's all, we can go ahead and create a new
contract with this aggregator v3 interface. Or what we can do is
we can import this directly from GitHub. If we go back to the
documentation of these chainlink data feeds go to using data
feeds, we scroll down, we see at the top, we have this import
statement, import at chain link
slash contracts slash SFC visa
right interfaces aggregator v3 interface, this, this import is
has the same path setup as the GitHub repository for the chain
link code, instead of us directly adding all the code
right into our remix, what we can do instead is we can import
directly from GitHub, or what's called an NPM package. Remix is
smart enough to know that at chain link slash contracts, is
referring to the NPM package at chain link slash contracts,
we'll talk about NPM, a little bit in the
future, it's what's
known as a package manager, and can keep versions of different
contracts for us to directly import into our code bases at
chain link slash contracts is created directly from the
chainlink GitHub repository. So remix downloads this code from
NPM, which is created from this GitHub. So now we know that if
we import at chain link slash contracts, src, V 0.8 interfaces
aggregator v3 interface dot soul, this is the same as if we
had just stuck this whole contract right at the top o
f our
funding contract, which makes our code look a lot nicer. And
now we have this aggregator v3 interface that we can work with.
Okay, great. So now that we have a minimalistic interface, which
will give us the API, how do we actually go ahead and get the
price here? Well, documentation has a good example, if you want
to play with it and try to reverse engineer it as well.
Here's how we're going to do it. In our code. We're going to
create an aggregator v3 interface object called price
feed an
aggregator v3 variable called price feed, which is
going to equal to aggregator v3 interface contract at address
this address exactly the same as what we're doing down here.
We're assuming a contract at this address is going to have
all the functionality of this aggregator v3 interface, which
again, means it has this decimals function, this
description function version, get round data. And the
important one latest round data, which has the latest price at
this answer piece, what we can do now i
s we can call that
latest round data function on the price feed. So we'll say
price feed dot latest round data. Now if we look at the
interface, we see that this latest round data actually
doesn't return one variable, it returns a whole bunch of
different variables. And that's what we're going to return in
our contract. So we're going to put these parentheses, and we're
going to say, you went at round ID, we can even look right at
the documentation to see what else it returns int price, you
went
, you went started out, you went
timestamp. And then you went ad, answered and round. Now there's
a lot of code here. Since this function returns so many
different variables, we have to set something up to capture
them. However, all we care about is price. We don't care about
round Id started at timestamp or answered in round. So what we
can do is just remove them and just leave the commas. Now we have int price equals
price feed that latest round data. The reason that price is
an int 256. And n
ot a un 256 is because some prices or some data
feeds could be negative here, so that it's an int 256. So it can
stay flexible. Now that we have the price, this is going to be
price of eath in terms of USD. And we saw an example of this
before, it was around 3000. And it returned this number because
solidity doesn't work with decimals for a number of
reasons. But we just need to know that there are eight
decimal places associated with this price feed. If you want to
double check how many decimal
s there are, this contract has a
decimal function that you can call as well, that will tell you
exactly how many decimals are in this price feed. Now as we know,
message dot value is going to have 18 decimal places. Why does
it have 18 decimal places? Well, because one ether is equal to
1-234-567-8910 12345678 Is this massive number in way which has
18 zeros, which is equivalent to 1.12345 678-910-1234 5678. So we
want these to have the same decimal places, right? Because
right now this has eigh
t, this is 18. They're different units
right now. So to get them to match up, all we need to do is
return price times one, ie 10, or one raised to the 10th, which
is equal to 1123456789 10. Message dot value, though, is
going to be a UNT 256. And right now, price is an interval at
six. So why did we convert this value from n into 56. To a human
to 56? Well, we can do what's called typecasting, all we need
to do is add you into 56. And wrap this whole thing up between
these two parentheses, you c
an't typecast anything. But there are
some values like into 56 and you into 56, that can be easily
converted between the two. Now of course, since we're not
modifying any state with this get price function, we can make
this view and say it returns that you went to 36. And if we
save and compile, we go ahead and we get that checkmark. Now
math can be a little bit tricky. The first couple times you do it
in solidity. But the more you do it, the easier it becomes. And
in the future, we can always r
eference a function like this
to figure out okay, here's the easiest way for me to get this
number. Awesome. So now we have a get price function, which is
going to return a un 256, which is going to be the price of
Aetherium. In terms of USD, all we need to do is convert the
message dot value from Aetherium. To terms of dollars,
let's create this get conversion rate function. So this one,
we're going to take an input parameter of un 256 of eath
amount, it's going to be a public view function. An
d it's
going to return you went to 56, we're going to pass it some eath
amount. And on the other side, we're going to get how much that
eath is worth in terms of USD. So we're going to do a u
intuity. Six eath price equals get price. So first, we're going
to call our get price function that we just created to get the
price of Aetherium. Then we're going to do u and 256 eath
amount in USD equals eath price times eath amount, and then
we're going to divide it by 118. When you're doing multiplicati
on
and division math in solidity, you always want to multiply and
add first and then go ahead and divide since eath price and eath
amount both have 18 additional decimal places if we were to
just let them rock without this, they would have an additional 36
zeros tax tacked onto the end. So we need to divide by one EA
team. Now when we get to the hard hat sections of this course
testing all this math is going to be a lot easier. And if
you're really struggling with some of the math bits right now
,
I wouldn't let that slow you down. Because once we get to
heart, it's going to become a lot easier to actually test this
than working on a test net. And this eath amount in terms of USD
is the number that we're looking for. So we can just go ahead and
return eath amount in USD, CSB returns here, and boom, now we
have a good conversion rate function to walk you through the
math real quick. Let's say the eath price is going to be
$3,000. So it's going to be 3000. But it's going to have an
additi
onal 18 zeros tacked on the end, it matches the message
dot value way units. And let's say for example, we send one
eath or 112345 678-910-1234 5678 eath into this contract, one
eath should equal $3,000. So to get the price, we're going to
now do the eath price, which is 3000 times the eath amount,
which is this one, and then divide by one raised to the
18th. So method out we'll do 3123 1-234-567-8910 12345678
times we'd 1001 2345678 1-234-567-8910
times 112345678 1-234-567-8910. And now we divi
de that by
112345678 1-234-567-8910, which equals 2.9. Mmm, II 21, which
the calculator kind of messed up them a little bit, but 2.9, not
2.99, ie 21 means this has 21 decimal places. So it'd be
2.1 234-567-8910 1-234-567-8910 one, or
1-234-567-8910 12345678 2900 I 9.99 and a nine. And this is
actually exactly the reason why we don't do decimal math in
solidity, our calculator saw that massive number was having a
hard time getting it. So it ended up rounding that number to
2.999. And nine, when
we work exclusively with whole numbers
in solidity, we don't have a chance of losing that precision.
And in solidity, this is going to return exactly $3,000, which
is correct one Aetherium at $3,000 per Aetherium is going to
be $3,000. And like I said, since we're building this
contract, assuming we're going to be working on this test net,
we're not going to test this function on the test net,
because we're going to have to wait for that transaction to go
through. If you want to go ahead and dep
loy this and play around
with it yourself, you're more than welcome to Okay, great, now
we have a function called Get conversion rate that we can use
on our fund function to make sure we've sent enough message
dot value in our fund. So what we can do now is all we need to
do is to get conversion rate of message dot value needs to be
greater than the minimum USD. Of course, right now our minimum
USD is just in terms of 50. And we know that conversion rate is
going to return it with 18 zeros to re
present the decimal places,
our minimum USD amount needs to be upgraded to 10 to 50 times
one e 18. Or again, one times 10, raise the 18th, I'm going to
deploy this to a test net just to demonstrate it. But again,
you don't have to if you don't want to wait for this. So I'm
gonna go ahead and deploy this confirm. And now we have this
funny contract. If I don't say anything in value, and hit the
fun button, we're gonna get this gas estimation error failed.
This is kind of a blanket error, basical
ly saying, Hey, you can
go ahead and send this transaction if you want, it's
highly likely that it's not going to work. And the reason
that remix knows that it's probably not going to work is
because it can see this require and simulate the transaction and
say, Hey, you didn't send enough money with this. However, even
if we send some money, like 5000 way, it'll still give us this
error. Because that's not enough. Let's do the calculation
right now based on what the price of eath is. So we can
a
ctually go to data dot chain that link, we look and see
approximately what the price is. So it looks like the price of
Aetherium right now is about $3,000. And this might be
different for you depending on when you do that. So if the
price of Aetherium is $3,000, and our minimum is at least 50.
We could do 50 divided by 3000 0.016 eath should be
approximately enough. So if we go to our Aetherium converter,
and we do 0.016, we'll get how much that is in way, let's do
0.02 Just to make sure that we
're going to be over the
amount. So we'll paste that in. We'll change this to way. And
now if I hit the fun button, instead of us getting that error
popping up, it's going to actually go ahead and let us do
the fun function and we could confirm it and it wouldn't fail.
I'm going to reject it for now just because I don't really feel
like waiting for the transaction to go through. But great. We've
confirmed that our get conversion rate is one working
as intended, awesome, great work. So what's the
next bit of this
funding contract that we want to do? Well, when people actually
send money to this contract, we want to keep track of all the
people who send us money. So let's create some data
structures. To keep track. Let's create an array of addresses
called funders. And we'll keep adding all the funders who send
money to us. So we'll say an address array, or an address
list. We'll make it public funders. And anytime somebody
sends us money, and this actually does indeed go through,
we'll
add that funded server list. So we'll say funders dot
push message dot sender, like message dot value. Message dot
sender is an always available global keyword message dot value
stands for how much Aetherium or how much native blockchain
currency is sent. Message dot sender is the address of whoever
calls the font function. So if we're on Rigby message dot
sender is going to be equal to whatever address is calling that
function. Since our address is sending the ether, we're going
to add our addr
ess to this funders list. This way we can
keep track of all the wonderful donators who are donating to our
contract, then maybe we won't even make a mapping of addresses
to you and 250 sixes of addresses to how much money each
one of these people have actually sent. So we'll do
address to you and 256 public address to amount funded. And
when somebody funds our contract will say address to amount
funded of message dot sender equals message dot value. Now we
have a function where people can fund o
ur contract. And we can
set a value in terms of USD and we keep track of the different
funders who actually fund our contract. This is fantastic. Now
I know we've gone over a lot of really intense math and intense
stuff here. So so let's do a quick refresher of what we've
learned so far. Whenever we work with a contract, we always need
the API and the address when compiled an interface gives us
that minimalistic API to interact with contracts outside
of our project. When you combine these compil
ed interfaces with
an address, we can call the functions on that interface on
that contract. chain link. data feeds are a decentralized way to
get information about the real world. In this case, we're
getting the price of Aetherium in terms of USD from a
decentralized collective of chain link nodes. When working
with math and solidity. Decimals don't work. So we need to keep
that in mind when doing any type of math in solidity. And we need
to make sure we always have the correct units, so that o
ur math
makes sense. Message dot value, and message dot sender, our
globally available variables were message dot sender
represents the sender of the message or transaction. And
message dot value represents the number of ways sent with the
message. There's a whole bunch of different special variables
and functions that we can access at any time. And these are
available in the solidity documentation. Alright, great,
we've got a great way that we can actually start funding our
contract. But our co
de looks a little bit messy, we've got a
couple of different functions for getting the price and
working with these prices, is there a way to make this math a
lot easier to use, this is where we're going to introduce the
concept of a library. So what is a library, I definitely
recommend checking out solidity by example.org, as you're going
along with this course, as well, they've got some fantastic
examples. One of such example is going to be about libraries,
libraries are similar to contracts.
But you can't declare
any state variables and you can't send ether. We can also
use libraries to add more functionality to different
values. What do I mean by that? Well, what we can do actually is
we can have get conversion rate be a function of a un 256. So we
could do something like Messer dot value dot get convert John,
conversion rate. And we can add functions as if you went to 36
was an object or a struct or a contract that we actually
created. So how do we do this? Well, let's create a ne
w
contract in our contracts folder and create a new file. We're
going to call it price converter, dot salt. And our
price converted outsole is going to be a library that we're going
to attach to a un 256. So how do you actually create a library
and what is a library? Well, a library is going to be really,
really similar to a smart contract. It's gonna start with
spdx license identifier. My team, we're gonna give it a
pragma, solidity zero point 8.0. And instead of typing contract
for the name of
the contract, we're going to do library for
the name of the library. We're going to call it price
converter. Now libraries can't have any state variables and
they also can't send ether and all the functions in a library
are going to be in there. kernel. So what we can do is we
can go back to fund me dot soul, we can grab get price, get
version and get conversion rate, copy them all, delete them from
Funmi dot soul and paste them into our library. And of course,
since we're using aggregator v3 i
nterface in here, we can also
copy the import from Funmi. And since we're not using the
aggregate of three interface and our contract anymore, and we're
using our price converter, we can paste it into our price
converter. Now, if we compile price converted at soul, we see
that it actually passes. Now all the functions inside of our
library need to be internal. And we're gonna make this library
price converter different functions, we can call on you
activity six, for example, we're going to be ab
le to do message
dot value dot get conversion rate, we're going to directly be
able to get the conversion rate of a value of a U and 256 as if
that was a function for it the whole time. So first, let's make
this internal. Let's make get conversion rate internal. And
we'll make good version internal. Now that we have this
library price converter back in our Funmi, we can now import
this price converter and attach it to you in 256. So we'll do
import that slash price converter, dot soul. And in Fu
nmi, we'll do using
price converter for UNT 256. Of course, if we compile our Funmi.
Now, this line is getting an issue because saying hey, get
conversion rate isn't defined. Now in our library, the first
variable that gets passed to the function is going to be the
object that it's called on itself. So in Funmi dot soul,
let's go ahead and comment out this line for now. If we do
message dot value dot get conversion rate, this is
secretly the same as we did get conversion rate or message dot
valu
e in our price converter library, the message dot value
is going to be passed as the input parameter to get
conversion rate, forget price and get version we don't really
care about the number. So we're just going to leave it blank for
now. So instead of require get conversion rate of message dot
value, we can now do message dot value dot get conversion rate.
And compile that you'll see that here we're not passing a
variable, even though our get conversion rate function says
hey, I'm expecting a
variable. Again, the reason for this is,
is this message add value is considered the first parameter
for any of these library functions. And that's how it
works. If we wanted another variable in here, like you went
to 56, something else. Now we would want to pass something
else in here 123. And this 123 would get assigned to this
something else. But we're going to delete that for now. Okay,
great. And in doing that, we've minimized our Funmi contract a
lot by moving a lot of that math and price
conversion stuff into
our price converter library dot Sol. One of the most common
libraries that was used for the longest time was this library
called Safe math dot Sol. And you'll probably see it a lot of
different places, we're gonna go off on a quick little tangent
here and teach you about safe math. So let's close Funmi close
price converter. And let's create a new file called Safe
math tester. That's all. And let's start with some basic
stuff in here. Safe math was all over the place before
version
0.8 of solidity. And now it's almost in no contracts. What
happened, why is safe math no longer used as much? Well, let's
create a sample contract. This is a section that you don't have
to follow along if you don't want to code along with me. But
if you want to you absolutely still can. This is going to be a
contract we are going to deploy on a JavaScript virtual machine,
we can use any version of solidity before version 0.8 of
solidity. So for example, we use pragma, carrot, zero, poin
t 6.0.
And we'll create contract safe math tester, dot Sol. Now if I
create a you int eight, I set it to public big number. And I set
this to 255 Oops safe math tester. Let's go ahead and
compile safe math tester with zero point 6.7 pragma, solidity,
the maximum size of a Yewande eight is going to be 255. This
is going to be the biggest number that we can fit in the
new int eight. And if I were to deploy this to a JavaScript VM
or even a test network, safe math tester, let's go ahead to
play up.
If I hit big number, we're gonna get 255. Well, what
happens if I create a function called ADD? That sets big number
equal to big number plus one? Let's save that. Delete that old
contract and deploy. All right now big numbers 255 What happens
when we add one two? big number. When 255 is the max size a, UNT
eight can be? Well, let's hit add. Now let's check what big
number is. Big number gets reset to zero. So what's going on?
Well, prior to version 0.8, of solidity, unsigned integers and
integ
ers ran on this concept of being unchecked, which means
that if you passed the upper limit of a number, it would just
wrap around and start back from the lowest number it could be.
So if I call add a whole bunch more times, and hit big number,
now let's say if I were to hit this add button a ton more times
and get it back to two and a 55. It would then continue to wrap
over to zero. So one of the most popular libraries that was out
there was this safe math library, which would basically
check to
make sure that you weren't wrapping around a un 256
or an intuitive six, basically, it was a way to say, hey, you've
reached the max this number can be and now your transaction is
going to fail. If we switch this to 0.8, of solidity, delete the
old contract, go switch this to 0.8. We'll go ahead and compile
it. And now we deploy this to JavaScript VM. If I hit big number, we get to
under 55. But if we hit Add, it actually fails. And we still get
to under 55. In version 0.8, in solidity, they ad
ded this bit,
where it automatically checks to make sure if you're going to do
what's called overflow or underflow on a variable, we can
actually revert back to the unchecked version by using an
unchecked keyword. So if we wrap this big number equals big
number plus one in this unchecked bracket, let's delete
our old contract will compile will redeploy. We had big
numbers to 55. Now we hit add, we hit big number again, it
reverted back to zero. So that's a little bit more about safe
math, checke
d and unchecked. So in version 0.76, and below this
code that you see in front of you, is going to be the exact
same as this code in 0.8. And above with this unchecked
keyword. Now you might be thinking in newer versions of
solidity, why would I use this unchecked keyword? Well, you'll
find out later that this unchecked keyword makes your
code a little bit more gas efficient. So if you're
absolutely positive that your math is never going to reach the
top or bottom limits of a number, that it mig
ht make sense
for you to use the unchecked keyword. Let's head back over to
our Funmi contract, where we are now using the price converter
library that we just created. Alright, great. So now we've got
a pretty minimalistic contract here for actually doing the
funding. And we have all of our math for getting conversion
rates done in our library price converter, which we're going to
import at the top of Funmi. Cool. So at this point, we've
got our fun method. Awesome. And so we can allow anybody
to go
ahead and fund this contract and send this contract Aetherium, or
any native blockchain currency to this contract. Well, now what
do we want to do? Well, once all the funders have gone ahead and
funded, we're going to want the project to be able to withdraw
the funds out of this contract. So they can actually go ahead
and use those funds to buy things for this project. So
let's go ahead and create a withdrawal function. So we'll
create a function withdraw. And we'll make this public. Since
we're going to be withdrawing all the funds out of this
contract, we probably also want to reset our funders array, and
our address to amount funded. Since we'll be withdrawing all
the funds, those amounts should go back down to zero. So let's
go ahead and loop through the funders array and update our
mapping object so that each of these funders now has zero,
because in just a second, we're going to withdraw all the money
from them. So to do this, we're going to use something called a
for loop.
So what is a for loop? A for loop is a way to loop
through some type of index object or loop through some
range of numbers or just do a task a certain amount of times
repeating. So for example, let's say we have an array or list.
And on that list, we have 1234. If we wanted to get all of the
elements in this array, or in this list, okay, 1234. How do we
get all the elements in this list? Well, we would use a for
loop to loop through each one of these objects. So at zero with
index would be one
at the first index would be two, and at the
second index would be three, at the last index would be four. So
we would loop through the indexes zero through three to
get all these elements. Or maybe another example is if this was
A, B, C, D, A is at the zero with index B is at the first
index sees the second Indy's at the third and we will loop zero
through three. To get to each one of these elements, we're
gonna do that exact same thing. But with the funders array. So
how do we actually do that?
Well, we first start with the
for keyword, the for keyword says, Okay, we're about to start
a loop. And inside of these parentheses, we define how we
want to loop through it. Also backslash star, and star
backslash is sort of like brackets for comments. Anything
in between these two will be a comment. So in a for loop,
first, we give it the starting index, then we give it the
ending index, and then we give it the step amount. For example,
maybe we want to start with zero, we want to go to 10. A
nd
we want to go up by one each time. So we would go
01 234-567-8910. Or maybe we start at zero, we want to end at
10. And we go up by two each time. So we'd go 02468 10. Or
maybe we want to go from zero to five, we want to go from two to
five, with a step of one, we'd go 2345, etc. So this is what's
going to go inside of this four bit here. So for our starting
index, and let me even just put this above so that you can
reference it. So our starting index is going to be you in to
VT six variable,
and we're going to call it funder index. And
we're going to start with thunder index being equal to
zero. So we're starting with zero here. And we're going to
end with the length of our funders array, since we want to
loop through all of the funders. So we're going to say, funder
index needs to be less than funders dot length. So our
ending index is going to be whenever funders index is no
longer less than funders dot length. And then finally, we're
gonna say funder index equals funder index, p
lus one, which
means that every time the code inside of this loop finishes,
we're going to increase funder index by one. That's how we go
from zero to one to two to three to four to five, etc. Another
way you can type funder index equals funder index plus one is
you can just do funder index plus plus, this plus plus syntax
means funder index equals itself plus one. So let's start looping
through our funders array to access the the zero with element
or the first element, we're going to say, funde
rs of funder
index. So we're saying we want to access the zero with element
of our funders object. And this is going to return an address
for us to use, we're gonna go ahead and say address under
equals funders at the funder index. So now we have this
funder address. And we want to use this to reset our mapping.
So we're going to say, address to amount funded at at the
funder key is now equal to zero. Because remember, in fund we
update the amount. Whenever we fund the contract, when we
withdraw
the money from the contract, we're going to reset
it back to zero. Now let's walk through this funder index starts
from zero. So we're going to get the zero with funder, we're
going to grab that funder at the zero with index and we're going
to reset the address to male funded of that funder to zero,
then this for loop is going to update by one, it's going to
move from zero to one, it's going to check then if funder
index is less than the length, let's say funders has 10 people
in it. If funders
has 10 people in it, it'll still be less. So
now funder links will be one address funder will equal
funders of one now instead of zero, and we'll grab that
address, and we'll reset that addresses about funded to zero,
then we'll continue to two to three to four all the way up to
the length of our funders array. And this is how we can loop
through our objects. So saying this middle one is the ending
index isn't exactly right, since we're really checking for a
boolean to see if this is still true
, but hopefully you get the
idea. So we've reset the balances of the mapping.
However, we still haven't done two things, we still need to
reset the array to make the funders a blank array. And then
we also need to actually withdraw the funds. Since when
we funded this, we sent message dot value along with calling
this fund function. However, we didn't actually withdraw the
funds. So to reset the array, we could loop through it and delete
objects from this address array. Or we could Just totally
refresh this variable. So instead of
looping through the array and deleting objects, we're just
going to say funders equals a new address array, we're going
to completely reset the array by saying this font is variable now
equals a brand new address array with zero objects in it to
start, if we were to put a one here, this would be there'll be
one element to start in the array, two would be two, three
would be three, etc, we're just going to start it as a
completely blank new array. So great, we
've gone ahead and
reset the array. But how do we actually now with draw funds
from this contract? How do we send the funds back to whomever
is calling this now to actually send ether or send native
blockchain currency, there are actually three different ways to
do this, we're going to look at all three, and say what the
differences between the three of them are, the three different
ways are going to be transfer, send, and call. Let's go ahead
and start with transfer. Since transfer is the simpl
est and at
surface level makes the most sense to us. So if we want to
transfer the funds to whomever is calling this withdrawal
function, we would do we would say message dot sender, dot
transfer. And then we'd get the balance of our contract here by
saying address this, this keyword refers to this whole
contract dot balance, and we can get the native blockchain
currency or the Aetherium currency balance of this address
like this. And we can just do that only thing that we need to
do is we need
to cast we need to typecast message dot sender from
an address type to a payable address type. So message dot
sender is of type address. Whereas payable, message that
sender is of type, payable address, and in solidity in
order to send the native blockchain token like Aetherium,
you can only work with payable addresses. To do that, we just
wrap it in this payable type caster. So this is the first way
that we actually send Aetherium or send tokens from different
contracts to each other, we wrap t
he address that we want to send
it in, in this payable keyword, we do dot transfer, and then we
say exactly how much we want to transfer. But there are some
issues with transfer. Here we are on solidity by example, for
sending ether, which, again is a fantastic resource to refer to,
if you get lost, the method that we just looked at was this
transfer method. Now we saw way earlier in the course, that if I
sent Aetherium, from one address to another, it cost about 2100
gas or 2100. Gas, our trans
fer function is capped at 2300 gas.
And if more gas is used, it throws an error. The next one
that we're using is going to be sent which is also capped at
2300 gas. And if it fails, it'll return a Boolean. So with
transfer, if this line fails, it'll air and revert the
transaction with send, it won't air, it'll return a boolean of
whether or not it was successful. So using send will
do payable message that sender that send address this balance.
But we don't want to finish our call here. If this w
ere to fail,
the contract wouldn't revert the transaction, and we just want to
get our money sent. So we want to do Boolean send success
equals this whole bit here. And then we want to require success.
And if this send fails will throw an error saying sin
failed. This way, if this fails, we will
still revert by adding our require statement here. Transfer
automatically reverts if the transfer fails, send will only
revert the transaction if we add this require statement here. So
great. What's the
third way that we can actually send Etherium or
native currency wallets with this call command. Now call is
going to be one of the first lower level commands that we
actually use in our solidity code, because this call function
is actually incredibly powerful. And we can use it to call
virtually any function in all of Aetherium without even having to
Have the API we'll learn the advanced ways to use this call
much later. For now, we're just going to learn how to use it to
send Aetherium, or your
native blockchain currency call is
going to look very similar to send, we're going to do payable,
message dot sender, dot call. And this is where we will put
any function information or any information about the function,
we want to call on some other contract, we actually don't want
to call a function. So we're going to leave this blank, we
can show that we're leaving it blank by just putting in these
two quotes here, we instead want to use this like a transaction.
And as we saw in our deploym
ent, there's always this message dot
value bid, we're going to use this call function as if it's a
regular transaction, and we can add stuff like message dot
value. So in here, we're going to add these squiggly brackets.
And we're going to say, value address this dot balance, this
call function returns actually two variables. And when a
function returns two variables, we can show that by placing them
into parentheses on the left hand side, the two variables, it
returns are going to be a Boolean,
that we're going to
call call success. And also a bytes object called data
returned, since call allows us to actually call different
functions. If that function returns some data or returns
value, we're going to save that in the data returned variable.
It also returns call success, where if the function was
successfully called this will be true. If not, this will be
false. And since bytes objects are arrays, data returns needs
to be in memory. Now for our code here, we're actually not
calling a
function, so we don't really care about data returned.
So similar to what we saw with the price contract, we can just
go ahead and delete that and leave the comma to tell solidity
Yeah, we know this function returns two variables, but we
only care about one. And then similar to the centerpiece
above, we're going to do require cost access call failed, meaning
that we're requiring cost accesses true. Otherwise, we'll
revert with an error that says call failed. Now of learning the
difference betwe
en these three is a little complicated for you
right now. Don't let that slow you down. Feel free to come back
to this after you've learned more about how some of these
lower level functions work. And a little bit more about how
gasworks solidity by example, does a fantastic job though of
saying what the difference is between all three, our transfer
has a maximum of 2300 gas and throws an error if it fails,
send has a maximum of 2300 gas returns a Boolean, if it fails,
call forwards all gas so d
oesn't have a capped gas. And similar
to send returns a Boolean, if it is successful, or if it fails,
As of recording right now using call is the recommended way to
actually send and receive Aetherium or your blockchain
native token for now, if this part's a little bit confusing
for you, for now, just look at this and see ah, that's how we
send and transfer Aetherium or native blockchain currency
tokens. And I'm going to delete this part for the video, but
I'll keep those comments in the code re
pository associated with
this course. And okay, perfect. If we hit compile a fun meat
outsole, we do indeed see that it's passing compliation.
However, there's a bit of an issue here, right now, anybody can withdraw from this
contract. So anybody can fund, which is what we want. But we
don't want anyone to be able to withdraw. We only want the
person who's collecting the funds to be able to actually
withdraw the funds. So how do we set this up so that the withdraw
function is only called by the
owner of this contract? Well, to
do that, we're going to set up a couple new functions. So when we
deploy this contract, we want to automatically set it up so that
whomever deploys this contract is going to be the owner of this
contract. And then we can do some parameters to set it up so
that only the owner of this contract can call the withdrawal
function. So how would we do that? Well, maybe we can create
a function called like, call me right away. And right after we
deploy this contract, we c
all this call me right away
function, which will set up us as the owner. Now that's going
to take two transactions. And that would be really annoying if
we had to do that. So instead, solidity has something called a
constructor. And if you're familiar with other programming
language, a constructor is exactly the same as other
programming languages. constructor is going to be the
function that gets called when, immediately whenever or you
deploy a contract. So if I were to deploy Funmi, dot Sol,
and I
were to say, minimum USD equals to minimum USD would no longer
be 50 times one e to the 18th, it would be immediately updated
to two. Because constructor is a function that gets immediately
called in the same transaction, that we create this contract,
this constructor function is going to be incredibly helpful
for us, because it allows us to set up the contract the way we
want it to be. So for example, if we want the withdrawal
function to only be able to be called by the owner of this
con
tract, we can have the constructor set up who the owner
of the contract is. So let's create a global variable called
address, public owner. And then in our constructor, we'll say
the owner is going to be equal to the message dot sender. The
message that sender of the constructor function is going to
be whoever is deploying the contract. So owner is now going
to be whoever deployed this contract. And don't worry, we're
going to demo all this very soon and show you everything that's
going on with
ether scan everything, demoing this all
right now might take a little bit of time, because we're using
a test net. So if you want to test it all right now,
absolutely go for it, but just know, it'll take you a little
bit longer to do so. Now that we have the owner setup, we can
modify our withdrawal function to make it so that only the
owner can actually call this withdrawal function. So at the
top of the withdrawal function, maybe we want to add a section,
maybe we want to say require message d
ot sender equals the
owner, a note about double equals versus equals, you can
think of this single equals as a set parameter. So when I say
owner, is now set to message dot sender, double equals is how you
check to see if these two variables are equivalent. So
we're saying is message dot sender, the same as owner. So
this is checking to see equivalence. This is setting
checking to see equivalence setting. So we're gonna say
require message dot sender is equal to owner, otherwise, we're
gonna thr
ow an error, saying, sender is not owner. Perfect.
Now we have a quick way to make sure the withdrawal function is
only called by the owner of this contract. Now, let's say that
there's a lot of functions that we have in this contract that
are going to be required to be the owner, maybe there's a lot
of functions in this contract that need a whole lot of
different requires, we don't want to have to copy paste this
line to every single one of our functions. So what can we do?
Well, this is where
something called modifiers come in. So for
now, we're gonna go ahead and delete this line. And below,
we're going to create something called a modifier, our modifier
is going to be a key word that we can add right in the function
declaration. To modify the function with that
functionality, we're going to create a modifier and call it
only owner. And we're going to paste that line that we just
made in withdraw. And underneath that, we're going to put a
little underscore, what I can do now is I ca
n take this only
owner modifier, and stick it in the function declaration of my
withdrawal function. So what's happening with this modifier
with only owner in this function declaration, we're saying, hey, for this
withdrawal function, before you read all this code inside of it,
look down at the only owner modifier and do whatever is in
there first, and then do whatever's in the underscore,
this underscore represents doing the rest of the code. So now,
when we go call the withdraw function, we ac
tually do this
require statement first, and then call the rest of the code.
If this require statement, we're below the underscore, this would
tell our function to go ahead and do all this code first. And
then run the require. Because again, we have this only only
keyword, we're saying, Great, we've got a function, it's
withdraw its public, oh, only odor modifier. Let's look at how
that works. Okay, it tells us how to do all the code of the
original function first. So let's go ahead and do that.
Okay, now we're done. Now, what do we do? Okay, now we'll run
the require, we want to go ahead and put the require here first.
So this is how modifiers work, and how we can use them to
improve our functionality. All right, awesome. We have all the
basic functionality of our contract that we need here. Now
we're finally actually going to run everything on a test and see
everything happened live before our eyes. Are you ready? Let's
do this. So let's go over to the deploy
tab. And we're going to s
witch of course to injected web three.
Remember But we're using injected web three, because our
price converter dot soul is using chainlink Oracle's that
actually exist and are actually monitoring the Rinkeby network
for us. Now we're going to scroll down to, and we're going
to choose the Funmi contract. And we're going to go ahead and
deploy. Once again, we want to make sure we're on the Rinkeby
test net. And we have a little bit of rinky eath in our wallet,
let's go ahead and deploy, confirm.
And we'll wait a little
bit. And I'll pull up our log here. And we'll wait a little
bit for our contract to get deployed. Alright, great. It
looks like our contract has indeed been deployed. If we
scroll down, we can see all of our functionality, minimum USD
is going to be that $50. But with 18 zeros so that the units
are the same as Aetherium, the owner of our contract was set to
our address, the instant we deployed this contract, it was
deployed by calling our constructor function. So this
add
ress 0x 106 x is going to be the same as the address in our
meta mask, your address here, of course, is going to be a little
bit different than mine, we have our funders array, which, of
course is going to be blank, we have our address array, which is
also going to be blank. And then we have two functions that we
can use to modify the state of the blockchain withdraw is going
to be orange, because we're not paying any Aetherium we're
actually gaining Aetherium or whatever native blockchain
curre
ncy fund is going to be read, because fun is a payable
function that we are going to be sending Aetherium to or sending
whatever native blockchain currency that you're working
with. So let's go ahead and see how this all works. So first,
let's go ahead and fund this contract. Again, funding, we got
to do a little bit of math, right now, since the price of
eath is around $3,000. And we're looking for $50. Minimum, we do
50 divided by 3000. We can do 3000 divided by 50, we can do 50
divided by 300
0. So we know that around 0.02 Aetherium, should be
enough for this contract to work. So 0.02 Aetherium is this
much way, we can copy that, paste that into here. So when we
hit fund, it should actually pass. And we do indeed see meta
masks pop up. And we'll go ahead and confirm. Once this
transaction goes through, we'll be able to see this contract on
ether scan with the funds in it. Now if we don't add way here, if
we don't add a value, once again, we had fund will say gas
estimation failed bec
ause we're not sending enough here. And in
fact, we even see execution reverted didn't send enough, we
can absolutely send this transaction however, it's going
to fail. Great. So now that our transaction has gone through, if
we go on to the rink the ether scan, once we wait a little bit
for it to finish indexing here on the Rinkeby ether scan, we
can see that transaction actually went through for doing
the funding, we can actually see a lot of different details going
on with this transaction as
well. And if we scroll down, we
once again, you can see all the information about us calling
this fun function, gas limit gas, you should the gas fees,
gas price, and we can see the input data as well, we can see
that we called the fund function down here. We'll learn more
about the input data later. If we go to the contract that we
deployed, we can now see two transactions, we can see our
contract created transaction. And we can also see we called a
font method. And if we look at the balance of
our contract, and
now has 0.02 ether, which makes sense, since we just sent it
0.02. If we put that 0.02 eath in terms of way back into the
value section, and we call font again, after this transaction
goes through, we should see this number go from 0.02 to 0.04. Now
after we wait a brief delay, we do indeed see the balance has
gone up to 0.04, which is exactly what we'd expect.
Awesome. So our funding mechanism is working correctly.
And if we go down into our array, and our mapping to do addre
ss to array, and we
paste our address, we should see the phone number show up. And if
we go to funders of zero, we see our address. And if we go to
funders of one, we also see our address and we see that and we
see this call going through if we go to funders have to in our
log over here, we actually see that we get an error an
optimization that we could make to our contract in the future is
to check to see if an address is already in the funders array and
then not add it if it's already there. N
ow let's go ahead and
try to call this withdrawal function. But let's try to call
it with a different address than the address we originally
deployed this contract with. So to do that, scroll all the way
to the top of this here. And I'm going to go to my Metamask. And
I'm going to switch to a second account and hit connect. Now,
our remixes should be updated with the new account that's in
here, you'll see that if I switch back to account one, it
switches back to account one, so long as they're b
oth connected,
you can see which accounts are connected to applications by
clicking this connected button and see which ones are
connected. If you ever want to disconnect that account, you can
click the three little dots and it disconnect the account. And
now we can see that this account is not connected. However, count
two is connected. Let's go back to account one and Connect
account one so that both of them are now connected. But we'll
switch to account two, let's switch to account two, becau
se
again, account two isn't the owner of this contract. We
scroll down to owner, we can see owner's 0x 1066 Something
something and account two is 0x 043. Something something
awesome. So what do you think will happen when we hit withdraw
here? Well, our modifier only owner should kick in. And we
should get notified that if we send the transaction, we'll get
this error sender is not owner. So let's try it. Ah gas
estimation failed, we do get this error. Sender is not owner,
which is perfect. This
is exactly what we want, we could
absolutely send this transaction if we wanted to. But that would
just be a waste of gas because this transaction is going to
fail. However, if we switch back to account one, and we hit
withdraw, meta mask will pop up, enabling us to confirm and
withdraw the ether out of this contract address. Now if we look
at this contract address on ether scan, after a brief delay,
we'll see the balance go from 0.04 back down to zero. And
we'll see our wallet balance go up fr
om what it is back to 0.04
plus what it was. And after a brief delay, you can see our
balance is indeed back down to zero in our contract. Now, if we
do address to amount for our wallet address that was doing
the funding, it's back down to zero. And if we try to check the
address of funders at index zero, we get called to funders
that fund me errored execution reverted. We've completed all the basics
of this section that I wanted to go through. And you should be
incredibly proud of yourself for
getting this far, you've just
deployed a really advanced smart contract, we're using a library
and chainlink contracts to build some of the most powerful
applications in the planet, we've learned to use a library
for any type we want in our smart contracts. We've learned
more about multiplication, and then units of measure in
solidity and smart contracts. We've learned about mappings
more about arrays what the constructor does, we've learned
how to send money we've learned about for loops, we've
learned
about the different ways we can actually send money, at least
from a low level. And we've learned about modifiers. This
section is one of the tougher sections in this course. So if
you completed this, you should be incredibly excited. We're
going to go through our code now. And we're going to make a
number of tweaks. Now this section, we are going to do a
little bit more advanced solidity here. So if you get a
little bit lost, don't sweat it too much. And feel free to try
some of this s
tuff in the future on your own. We're going to
modify this contract to make it a little bit more professional.
It's not going to be end to end amazing, but it's going to be a
little bit better. And you'll see why in a minute. So the
first thing that we're going to do is we're looking we're going
to look at some of these variables here. In particular
owner and minimum USD, owner gets set one time in our
contract here. And then it never changes. Again, minimum USD gets
set one time, even outside o
f the constructor. If we have
variables that only get set one time, we can actually use some
tools in solidity to make them more gas efficient. For now
let's compile our Funmi contract, and then deploy it to
a JavaScript virtual machine. Remember, we can go ahead and
deploy it right now. However, funding and withdrawing and
doing in the money stuff isn't going to work. Because again, we
don't have a chain link network on our JavaScript VM. So those
aren't going to work so well. But for what we'r
e gonna do
right now, we don't really care so much. Here's what we do care
about. You do care about how much gas this costs to actually
send. We do care about how much gas is costs to create right now
creating this contract costs about 859,000 gas and we're
going to Add a couple of tricks. Right now to bring this number
down, we're going to add some stuff back in in the bid, which
will bring it back up. But for now, we're going to learn some
tricks to bring this number down. The two tricks that
we're
going to learn are the constant keyword and the immutable
keyword, in their solidity. There are two keywords that make
it so that your variables can't be changed. And those keywords
are constant and immutable. You can learn more about them in the
solidity documentation, if you assign a variable once outside
of a function, and then never change it, so if if it's
assigned at compile time, you can go ahead and add this
constant keyword. We'll learn later about storage. But when
you add a cons
tant keyword, this minimum USD no longer takes up a
storage spot, and is much easier to read too. So now we recompile
this, and we deploy this new contract. Let's see if we saved
any gas. We look in the transaction logs, now, we can
grab the transaction cost of how much this cost to deploy, let's
compare it to how much it was before. Well, we saved almost
19,000 gas, that's almost as much gas as a cost to send
Aetherium. Typically, constant variables have a different
naming convention. Typically
, you'll want to do them all caps
like Min imaam. Underscore, who is the CIO all caps with
underscores. So now let's just find minimum use D, and replace
that with all caps as well. With this interaction, we know that
this variable is a constant variable, and it's much cheaper
to read from now, if we go ahead and compile this and redeploy.
In our Funmi contract, even though this is a view function,
remember, view functions do have gas costs when when called by
contract as a constant variable, we
can see the execution cost of
this variable 21,415 gas. So let's put a little note rather
Nathan. If we remove the constant
variable, we delete this contract. And we redeploy, like
Funmi. And we hit minimum ust again, we can now see how much
gas this was cost. If it wasn't a constant variable, we can see
the gas cost did indeed go up. Now on chains that are much
cheaper, this gas difference probably won't make that much of
a difference. But on more expensive chains like Aetherium,
this is going
to make a big difference. For example, on
Aetherium, we can actually see current gas prices on Aetherium.
Here, we can see the current gas price of Aetherium is about 141
way, so we'll go to our converter, way to way we'll copy
the way price times this will get the gas price of calling our
minimum USD, which is this number here, which if we put
back in our Aetherium uniconverter, we can see cost
this much gas. And if we times that by the approximate current
price of Aetherium, which is around $
3,000. Calling minimum
use D as a constant is going to cost $9 on the inside is at a
nonconstant is going to cost almost an entire dollar more,
you can see how all these little gas optimization tricks are
going to make your life a lot better. So let's keep this
constant keyword in here. We'll learn more about constant and
storage in later sections of this course. Now, as you're just
getting started with this course, and with slip the Do not
struggle. And do not worry about making your contracts
as gas
efficient as possible in the beginning, and especially right
now. Just write your contracts as best as you can. Once you get
really good at gas. And once you get much later on in the course,
and much more advanced with solidity, then you can start
going back and working on gas optimizations. But do not let
gas optimizations hold you back. Or if you start stressing over
it, just let it go. Don't worry about it and just write your
code as best you can. So long story short, Do not stress abo
ut
gas optimizations right now. Now another variable we only set one
time is going to be our owner variable. Owner equals message
dot sender. We set this one time in the constructor variables
that we set one time but outside of the same line that they're
declared. And we set them for example, in the constructor, we
can mark as a mutable typically a good convention for marking
immutable variables is going to be doing I underscore so that we
know that these are immutable variables, they have very
similar gas savings to the constant keyword. Owner, of
course, is a variable that we can't set on the line here
because inside the global scope, there's no function going on.
However, inside functions, because inside the global scope,
there's going to be no message that sender, there's only going
to be a message dot sender when we're inside of a function. So
inside here, we might say I owner equals message dot sender.
And then of course, we'll scroll down and we'll change this
require only owner
now equals i owner. Now if we compile that
deploy up, we can see how much gas we can see how much gas
calling I owner is going to be by with immutable notice, we get
21,508, which we'll go ahead and copy for now. And we'll put
right here, we'll say, immutable. Now, if we remove the
immutable keyword, let's close this redeploy. If we scroll down
to I owner, screw up the logs, we go down to the call, scroll
down, we see the execution cost was much more. So we'll do the
backslash, a seven here, ye
s, or non immutable. So you want to
keep some of these tricks in mind, when it comes to storing
variables. The reason that these two save gas is because instead
of storing these variables inside of a storage slot, we
actually store them directly into the bytecode of the
contract. And like I said, don't worry too much about that for
now, later on in the course, we'll teach you more about
storage and a lot of this low level stuff that comes to these
contracts. But for now, just know that these exi
st. And
they're nice gas savers, if you're only setting your
variables once. Alright, great. So we've just made our contract
a little bit more gas efficient, little gas efficiency
improvements are going to be concept I sprinkled throughout
this course. And when we get to the more advanced section, I'm
going to break down exactly what's going on and why all
these gas efficiencies exist and what's going on behind the
scenes for these gas efficiencies to occur. It's a
little bit in the weeds, which
is why I'm going to gloss over
it right now. So if it's confusing, don't
worry, I wouldn't let these gas efficiencies be the thing that
slow you down. Awesome. So we have these two gas
optimizations? How else can we make this contract a little bit
more gas efficient? Well, one of the ways we can make this more
gas efficient, is by updating our requires right now with our
require statement, we actually have to store this sender is not
an owner as a string array, every single one of these
charact
ers in this errorlog needs to get stored
individually, this string may not seem very big, but it's a
lot bigger than the alternative with what we can do. As of zero,
point 8.4 of solidity, you can now actually do custom errors.
For our reverts, we declare them at the top and then use ifs,
instead of require and then just add a revert statements. This
ends up saving a lot of gas, since we just call the error
code, as opposed to calling the entire string associated with
the air. So for example, wi
th our require down here, and with
actually with all of our requires, well we could do is
instead of having this require we could create a custom error.
So at the top, what we could do is we could say error, not
owner. And you'll notice that this is actually outside of the
contract here. Now what we can do is we can take this error not
owner scroll down into our only owner, instead of doing a
require we'll do an if statement, we'll say if message
dot sender is not owner, then we're going to go a
head and
revert with a non owner error. This ends up saving us a lot of
gas, since we don't have to store and emit this long string
here. Now in a lot of code today, you'll still see require
a lot of places because these these custom errors are pretty
new in solidity. So you'll want to get used to writing in both
ways. I wouldn't be surprised if in the future, the syntax for
some of these errors looks like this so that it's more readable.
But for now, if you want to do a more gas efficient way t
han
required, you can use something like this, we could update all
of our requires here for these customers. But for now, I'm
going to leave both in just to show you the differences. This
revert keyword does the exact same thing that required us
without the conditional beforehand. So you can actually
go ahead and revert any transaction or any function call
in the middle of the function call. Now let's look at one more
way to improve this contract. Sometimes people will try to
interact with the c
ontract that takes Aetherium or the native
blockchain token without actually going through the
required function calls that that are needed. For example, on
a JavaScript EVM here, I could actually try to send this
contract money without calling the fund function. However, if I
were to do that, what would happen with our fund function
get triggered? No, it wouldn't get triggered, we wouldn't keep
track of that funder, we wouldn't have that person's
information updated in this contract. So if late
r on we want
to give rewards or something we wouldn't know about those
funders. And this wouldn't be great because people would send
our contract money without us ever knowing and we wouldn't be
able to give them any credit or anything. Additionally, maybe
they called the wrong function by accident, and they they
weren't using Metamask. And they weren't using a tool to tell
them, hey, this transaction is likely going to fail. So what
can we do in this case? What happens if someone sends this
con
tract, ether without calling the fun function? Right now, if
we were to send this Funmi contract, ie it would just go to
the contract Right, and this contract just wouldn't keep
track of those people. But there's actually a way for when
people send money to this contract, or people call a
function that doesn't exist for us to still trigger some code.
And now there are two special functions in solidity one is
called receive, and one is called the fallback. Now in
solidity, there are actually a nu
mber of special functions and
two of these special functions are the receive special
function. And the fallback special function, a contract can
have at most one receive function declared using the
received external payable. Without the function keyword.
This function cannot have arguments cannot return anything
and must have external visibility, and a payable state
mutability. What does that actually mean? And or look like?
Well, let's create a separate contract to go ahead and play
with this.
So in here, we're going to create a new file
called fallback example, dot soul. And in here, we're going
to add our basic pieces, SPX license identifier, MIT pragma,
solidity zero, point 8.7. And we'll do contract fallback
example, like so, feel free to pause the video to catch up to
this point, let's recreate our fallback contract, let's create
a variable to go ahead and try to test this function, we'll
create a un 256 public result variable. And let's create this
receive function. So we'll say
receive, it's going to be an
external payable. function, we don't add the
function keyword for receive, since solidity knows that
receive is a special function whenever we send Aetherium or
make a transaction to this contract. Now, as long as
there's no data associated with that transaction, this receive
function will get triggered. What we can do in here now is we
can say result equals one. So let's go ahead and test this out
on the JavaScript virtual machine, we compile this, so
we're gonna g
o ahead and compile this. And we'll go deploy it on
the Java Virtual Machine, we're going to deploy our fallback
example. And we're going to see what result is initialized to,
since we haven't set anything for result, result, of course,
is initialized zero. But what if we were to send this contract
some Aetherium? Well receive would go ahead and be triggered
here, we can actually send this contract some material directly
by working with this low level interactions. But here, don't
worry about wh
at call data means for now, just know that this
area down here is a way we can send and work with different
functions. And we can add parameters to this transaction,
by going up here and adjusting the variables up here. If we
keep called Data blank, it'll be the same as if we were in
Metamask. And just hitting send in the choosing this contract
address. Again, we can't actually use Metamask, since
this is a virtual machine, and not one of the networks that
we're working with. So if I do, for exa
mple, I change this value
to one way, and I keep everything blank. And I go ahead
and hit this transaction button, which again, is going to be the
same as hitting this Send button, but only sending one
way. What do you think will happen? Well, let's try it. We
can see in the log area that we did indeed send a transaction.
And if you look at the description here, you can even
see it says from so and so to fall back example, dot receive,
it looks like it called our received function, which should
have updated our result to one. So if we hit result, now we can
indeed see that result has been updated to the value of one.
Well, let's go ahead and delete this. Let's deploy this contract
again. And this time, let's have this value be zero, does receive
get triggered this time. So let's pull this down. Let's hit
transact. Let's leave the call data blank, we'll leave value at
zero. So this will be the same as if we had sent zero Aetherium
to this contract. Let's hit transact. It looks like that
went through, do you think result is going to be one or
zero? You thought one you were correct or receive function gets
triggered anytime we send a transaction to this contract
now, and we don't specify a function. And we keep the call
data blank when working with any other contract like Funmi. For
example, when we call one of these functions, we're actually
just populating this call data bit with certain data that
points to one of these functions up here. If we send a
transaction and we add da
ta to it, we could actually call one
of these functions. Now let's try this again. Let's delete the
contract again we'll redeploy open this up result is currently
zero receive like I said only is triggered if our call data to it
is blank. Now this time if I had some call data to this
transaction, do you think receive will be triggered this
time? If we hit transact and remix we actually get a pop up
saying fallback function is not defined. This is because
whenever data is sent with a transaction
solidity says, Oh,
well, since you're sending data you're not looking for receive,
you're looking for some function. So let me look for
that function for you. Hmm, I don't see any function that
matches the 0x 00. So I'm going to look for your fallback
function. Remix is smart enough to know that we don't have a
fallback function. The second special function in solidity is
called the fallback function. This is very similar to the
receive function, except for the fact that can work even when
data
is sent along with transaction. So our fallback
will look something like this callback, external payable. result equals
to Fallback is another one of these functions where we're not
going to put the function selector because solidity is
expecting this, actually, you're already familiar with one other
special function, we go back to our Funmi. Our constructor, for
example, is a another type of special function. There's no
function keyword. solidity knows that this constructor is
immediately calle
d when we deploy this contract. So now we
have our fallback function. Let's go ahead and compile this.
Let's delete our old contract. Let's go ahead and deploy this
new contract. Like here, we hit result, we do indeed, see, it's
set to zero. Now, if I add this 0x 00, and I send this, and I
hit transact, this is equivalent to calling our contract here
without a valid function. So our contract goes, Huh, I don't
recognize what you're trying to tell me here, I'm going to refer
you to our fallback.
And now if we hit result, we see that it's
been updated to two. If we take this away, solidity will go,
Hmm, it looks like you're trying to send some Aetherium, or call
this contract without specifying what you want to do. Well, I
have a receipt function. So I'm just gonna go ahead and forward
you to that. So if we call transact, we hit result, we see
it updates back to one, add some data, hit transact, we see it
updates to to no data, updates to one slowly by example, that
org has a wonderful l
ittle chart that we can use to figure out
whether or not receive is going to get triggered, or Fallback is
going to get triggered. If it is empty, and there's a receive
function, it'll call the receive function. If it is data, and
there's no receive function, it'll just go to the fallback
function. And if there's no fallback function, it might just
it might air out. So this is a lot of really fantastic
information here. How can we apply this to our fund mi
contract here, or what we can do now in
our Funmi is we can add
these fallback and receive functions, just in case somebody
actually sends us contract money instead of calling the fund
function correctly. So what we can do is let's add a receive
function. So if somebody accidentally sends it money, we
can still process the transaction will say receive is
going to be external payable. And we'll just have the receive
function call fund. And we'll do the same thing with our fallback
function will have fallback external payable. We'll ju
st
have it automatically call fund. Now, if somebody accidentally
sends us money without calling our fund function, it'll still
automatically route them over to the fund function. This means to
that if somebody doesn't send us enough funding, it'll, that
transaction will still get reverted. So let's go ahead now.
And let's switch to rink B to test this on a real test net,
Amman rink B and my Metamask. Let's switch over to injected
web three. And we'll scroll down we'll choose our Funmi contract.
And we'll go ahead and deploy this Metamask pops up, I'm gonna
go ahead and confirm the transaction. And we see our
Funmi contract here right now we can see the owner we can see I'm
the owner, we can see minimum USD. And we can see of course
that it's a blank contract, and there's nothing funded in here.
If we the copy the address, and then go to rink the ether scan,
paste the address in, we can see that there's no ether in here.
And the only transaction associated with this has been
the contra
ct creation. We saw what happened before when we hit
the fun function, our contract was updated with a new balance,
and that funder was added to our an array. Let's see what happens
now if we just directly send this contract money without
calling the fund function here. If we did this right, our
receive function should pick it up and kick the transaction over
the font. So let's copy this address. We'll go to our meta
mask. We'll hit send, paste the address in here with 0.02 eath.
Again, because
this should be more than the minimum amount in
USD, we'll hit next. I'll go ahead and confirm this. After a
slight delay, if we did this right, we should see the
transaction having called the fund function here, now that our
transaction has gone through After a brief delay in waiting
for ethers can update, we do indeed see that our balance has
updated to 0.02, which of course, this makes sense. And we
see in the transactions list here, we see that this actually
went through as a, as a transfer i
nstead of us calling the fund
function. Let's go ahead and remix and see if our funders was
updated. It looks like it was at the zero
with position of funders, we have our address. And if we take
our address and pop it into address to amount funded, we can
see exactly how much we had funded. This means that since we
added this receive function in here, we automatically had to
call our fun function up here. So awesome work, we were able to
add a receive function to help people who accidentally ca
ll the
wrong function or accidentally send this contract money,
instead of correctly calling the fun function. Now, if they had
directly called the fun function, it would have cost
them a little bit less gas, but at least this time, they're
gonna get credit and add it to our funders array for having
sent our funding contract money. We've even learned some advanced
sections of solidity. And this is going to be the last time
that we start our projects in remix, we're going to be moving
over to a c
ode editor now, where we can get even more advanced
with our solidity on our setups. For the most part, you've gone
over the vast majority of solidity basics, there are a
number of things that we still haven't learned yet. And the
reason we haven't gotten into them is because they get more
advanced. And understanding the real use doesn't really make too
much sense until a little bit later. Some of the things that
we're going to go over are itams events, try catch function
selectors, abi encoding
, hashing, and then you will slash
and then you will slash assembly. However, if you've
gotten this far, you probably can read most solidity code and
understand what's going on, which is absolutely fantastic.
So you should give yourself a huge round of applause for
getting this far. And doing this. Let's do a quick summary
of this more advanced section and make sure we understand what
we learned in solidity there are a couple special functions. Some
of them are receive fallback, and Constructor.
These functions
don't need to have the function keyword. And instead it can just
be called like so. Receive and fallback are two very special
functions. If data is sent with a transaction, and no function
was specified, the transaction will default to the fallback
function if that fallback function exists. If data is
empty, and there's a receive function, it'll call the receive
function. There are a couple of keywords that can help us save
gas in the long run. Some of those keywords are going t
o be
constant and immutable. Constant and immutable are for variables
that can only be declared and updated once. Once we say
minimum USD is 50 times 118. This minimum USD can never be
changed again. And this helps us save gas. Immutable can also
save gas similar to constant. However, immutable variables can
be declared one time in the constructor, once an immutable
variable is declared, it can't be changed later on. In fact, if
we even tried to update an immutable variable or constant
variable,
and we compiled, a compiler would give us an error
saying, Can't write to a mutable here. Or if we tried to change a
constant variable. Our compiler would say, Hey, you can't assign
to a constant variable, sorry. In remix, if we want to send
ether to a contract that's on the JavaScript virtual machine,
we can deploy that contract. And then in the contract, we can
just hit the transact button without any call data and update
the value that we send with the transaction. If call data is
blank, it
will trigger the receive function if it exists.
But if there's data that doesn't specify any of the other
functions, it'll trigger the fallback function Awesome, you've done
fantastically to get this far. And for this section before we
get started actually moving over to Hardhead. And moving over to
JavaScript and understanding why we need to do that. Let's
understand a little bit about getting help and running into
problems. So let's say we have our Funmi contract here that we
just worked on. A
nd we run into an error. Let's say for example,
we forgot the payable keyword, right, and we go ahead and
compile this compile fund, we did so. And we scroll down. And
we see obviously, we have two errors here, right, we're
getting some errors, and we scroll down. And we see type
error message dot value and call value can only be used and
payable public functions, make the function payable or using an
internal function to avoid this error. And then it goes ahead
and gives the line that's airing.
Now this error is pretty
clear, this error code is pretty clear. It's saying Hey, make the
function payable, or using an internal function to avoid this
error, right? This should be pretty easy to to add payable,
and then recompile and be good to go. And this is actually a
good example of what to do when you run into errors. When you
run into errors. The first thing you want to do is you want to
try to figure out exactly what's going on yourself based off of
what the error says. This one's pret
ty straightforward, but some
of them can be a little bit more obscure step one when trying to
get unblocked trying to tinker and figure out errors yourself,
right? Because maybe you go okay, I'll make this payable,
right? And you go to save. And then it gives a different error
saying, hey, you know, payable doesn't go here. You resave you
recompile it goes, Hey, we're still missing that payable
thing. First step is always going to be trying to tinker and
figure it out yourself. For this course,
I want you to limit
tinkering, slash triaging to 20 minutes, if you take more than
20 minutes to tinker and triage, then move on to the next step.
But I also want you to take at least 15 minutes yourself or be
100% Sure, you exhausted all options, you're completely out
of ideas. So typically try to tinker try some stuff for 15
minutes. And if you're under 15 minutes, and you're saying, Hey,
I'm 100% Certain I've tried everything that I can think of,
then you can move on to the next step. So step
one, when you run
into errors is always going to be tinker and try to pinpoint
exactly what's going on. Try to pinpoint exactly what's going
wrong. Step two, let's say you tinkered and you tried payable
all over the place, and you couldn't figure out what this
error was and how to debug this here. Step two is always going
to be Google the exact error and see if you can learn from that.
So zoom out a little bit, I'll roll my mouse over this, grab
this, copy it, or quotes around it, and do exactl
y that, in
Google search that exact error and take some time going through
Google going through Stack Overflow going through Stack
Exchange eath, and look to see if somebody has asked this
question already. And it looks like down here looks like
somebody has type error missed a value and call though can only
be used on PayPal public functions. And if we scroll
down, we see that somebody ran into exactly this. And they went
ahead and solved it. They go, I realized my mistake, I needed to
add the
PayPal keyword to my own implementation. And they go
ahead in this question, they've added the payable. And hopefully
this would give you the insight to say, Ah, okay, great. I do
need to come back here and add payable. Let's say this
StackOverflow question didn't show up. Right? This forum
wasn't here. What do we do next? So step one, tinker. Step two,
Google the exact error, I'm going to do a step 2.5 That only
is for this class, go to our GitHub, repo discussions, and or
updates for this cour
se, specifically, go to this GitHub
repo, full blockchain solidity course, Jas, it'll look a little
bit different when you all get to it. But come to this repo,
and look in this chronological update section to see if there's
an update on that section that you're doing. Obviously, since
I'm recording right now, there's no update. And if you don't get
anything, feel free to jump into the discussion section. And ask
a question in here, right, there's going to be a community
of people looking to hel
p each other out and looking to make
this a lot of fun. And the reason I say 2.5 is because in
the real world, you're not going to have our GitHub repo, when
working on stuff outside of this course, you're not going to have
this GitHub repo. So instead, in the real world, I'm still going
to give you the keys, I'm going to give you what it takes to
still unblock yourself on anything. Okay. So number three
is going to be asked a question on a forum, like stack, exchange
eath and Stack Overflow. St
ack Overflow is a question and
answering tech forum like this, right? You can ask tech
questions, and then you can answer them as well. And as you
can see, when you search for these issues, they'll show up so
Stack Overflow is more for general Role programming
questions and Stack Exchange Aetherium or Stack Exchange eath
Aetherium Stack Exchange, this is for more Aetherium or EVM
based question and all the solidity code that we're gonna
be working with, whether it's polygon, whether it's avalanc
he,
whether it's whatever, those questions are going to be valid
here, and you can ask here. So what you'd want to do is you'd
want to sign up or log in, and ask and format your questions on
these forms, you'll want to sign up for GitHub, you want to sign
up for Stack Exchange, you'll want to sign up for Stack
Overflow, so you can participate in these forums. In fact, if you
haven't already, let's sign up for GitHub right now. And let me
walk you through formatting one of these questions, becaus
e the
better you format your questions, the better chance you
have of actually getting the answer. And remember, when
asking questions on these forums, when asking questions,
in these discussion communities, people answer these questions
out of the goodness of their heart, right? So if you don't
get a response, there's a chance that maybe nobody knows, maybe
it's your question isn't formatted very well, and etc. So
we're going to learn how to ask really good questions here. And
if you're new to
blockchain, do not skip this section. Okay,
this is going to be that piece that's going to give you the
superpower to unblock yourself from any coding issue you run
into. So don't skip this powerup. Be sure to follow
along. Okay. So if you don't have a GitHub already, you do
need an email to get started. So I'm going to go ahead and sign
in, I made a burner account just for this video. So what we're
gonna do, we're gonna go ahead and sign up GitHub, enter your
email and we hit Create Account,
th
ey're going to send us an email. So we're going to come
back to our email, and we got our launch code here, paste it
in. And so a little bit of information. We're going to
choose the free version. and fantastic. We've now created a
GitHub profile. Now back over in the smart contract kid full
blockchain solidity course, Jas, I'm going to create a new
discussion, a new thread, and I want you all to comment on to
make sure you understand how to format and how to ask questions.
Okay, general thread
for practicing. Question formatting.
Oops. And so let's go back here. So first, I'm going to format
this question poorly, two ways, and then we're gonna format it
really, really well. So the first way we're going to format
it poorly, is by not giving enough information. So what
we're going to do is I'm gonna just copy this issue we're going
to do is we'll just say like, Hey, I'm having trouble with
remix and an error. Can someone help me? Why is this not a well
formatted question? If this is my
question, there's not nearly
enough information here, I, as a helper, have no idea what this
person's asking. So let's do something else. What I'm going
to do is I'm going to copy this entire contract all the fun,
read outsole, paste it in here, and go, Hi, I'm having issues
here. Can someone help? I'm gonna head start discussion here
want to hit start discussion, it formats this all weird. And once
again, there's not really enough information here. I don't know
what the issue is. But at least w
ith this one, we have some
code, we have some way to actually debug. So this is a
little bit better. But it's still not that good. Let's go
ahead and edit this. To make this even better. We hit three
dots, we can hit edit, what we can do is we can use something
called Markdown syntax, and highly recommend learning a
little bit of markdown, it's basically some syntax help make
discussions on GitHub. And also, questions on Stack Overflow and
Stack Exchange a lot easier. So we're going to format th
is code
by adding these three backticks at the start, and then also at
the end of our code. And then additionally, next, the first
three backticks, we're going to type so lid did T which tells
the format or to to use solidity to format this code here. Now if
we update discussion, we notice we get some nice highlighting
here. So this becomes much, much easier to read. Right? This is
way easier to read now than than it was before. However, it's
still not specific enough. We've given a ton of code
here, and we
haven't given the specific answer. So this is gonna be
really hard for somebody to answer. So let's make this more
specific. So let's edit this question again. And let's
specify, so we see here, our issue is specifically on this
function. We're going to copy this function and we're going to
delete everything else in here. And now we have just this code
inside of here. Now we're going to make this really specific.
We're going to say, on this function, I'm running into an
error. And t
hen what we're going to do is we're going to come
back here, we're going to roll the over this. We're going to
copy this we could have pull our code if we want but we're going
to format this error like this. And then we're gonna say Can
someone tell me What's going on? Update? Now this is a much, much
easier question to debug. Right? We have some minimalistic code,
we have the error that we're getting. And we have Can someone
tell me what's going on? Obviously, the answer to this
would be to add
payable to this. And that's what somebody would
say, hey, like, you need to add payable to this, I want you all
to practice doing some formatting, go ahead and add a
comment on this with your own formatted question. So that you
understand how to actually do the formatting. And this
markdown format. It's this format here, that's gonna be the
exact same for asking questions on Stack Overflow, or Stack
Exchange for this course, go ahead and practice if you want
to create more new discussions, feel
free to create new
discussions. If you want to use Stack Overflow or Stack Exchange
eath actually highly recommend you Stack Overflow or Stack
Exchange eath as well, because those are gonna get indexed a
lot better than GitHub here. However, feel free to ask
questions, obviously, in this GitHub as well. Now that I've
given you kind of the basics rundown, we're going to watch a
video that I made that goes even deeper into why and how to
format all these questions and what to use. So let's go ahe
ad and watch that. Every developer
has run into this. Something breaks or maybe you don't know
something, but you don't have time to let these stop you.
There are a series of steps that one should take to maximize
one's chances of solving any coding problem, but you'd be
surprised at how few developers currently use the superpower
effectively, our first one we'll spend the least time on, because
it's just tinker and experiment. When you run into an issue. Keep
trying different things you think m
ight work, maybe try
doing print statements throughout the file, learn some
debugging tips, but don't be so cocky that you only do this
first step. And this shouldn't just be random running around.
This should be trying to pinpoint exactly what's going
wrong in your code, so that you can either ask an effective
question, or figure it out yourself. So pinpoint exactly
what's going wrong, because you'll need it for the next
steps. Anyways, next, check the documentation. Not all tools
have good doc
umentation. But taking some time to explore
documentation can be a quick way to find your answer, you'll want
to learn how to search a webpage with Command F or Control F.
That way, you can look for specific keywords on a page, or
hopefully they have a good search bar that works well.
Sometimes documentation can be really dense. So maybe you'll
move to the next step, which is doing a web search. At the end
of the day, good software engineers are secretly just
professional Googlers. And this is o
ne of their most powerful
tools, being able to search the web for somebody else who has
already run into the problem that you've just run into, and
then solved it. Most search engines like Google have tools
you can use to get even more specific about what you're
looking for. Often for specific errors, the best thing to do is
actually just copy the exact error and paste it in the search
bar with quotes or use the Asterix in spots, your error
might be too specific. Most of the results you'll get w
ill be
from forums and q&a sites, which leads us to our next step,
asking questions in these forums and q&a sites. Just make sure
that before you ask a question, you've done some ample Googling
around yourself beforehand. This way you don't waste yours and
anybody else's time. And by asking questions that you swear
you will promise me that at some point, you will go back and help
other people learn as well got it good. Before even asking your
question though, we should learn where is going to be
the best
place to ask. This is why I've categorized for different types
of forums and QA sites, feel free to pause to read them over.
And here's some specific examples of each one of these
index code base forms like Stack Overflow, index repositories
like GitHub issues, index technology, specific forums,
like our slash eat dev or unindexed discussion platforms
like chainlink discord, one of the key differentiators in these
categories is the index keyword. We typically want to ask
questions on f
orums that web crawlers have gone through and
stuck them in their database or index them this way. And three
weeks when we look back at the code that we wrote, we can just
Google what was going on when we forget what it does. And this
will help out other developers who run into the same problem,
which in turn, they might go ahead and help you out later.
Ideally, most of your questions should be asked on one of these
index forms for this reason for their searchability and
discoverability. However
, at some questions are a better fit
for DMS, Twitter or discord that aren't index and we made a
little chart here to figure out where's the best place to post
your questions, feel free to pause the video take a look or
read our blog and the descriptions with the picture as
well to take another look at it. And of course, before actually
posting that in one of these forums, be sure to read their
rules as they might state that some kind of questions are
specifically for Ben. But basically the brea
kdown looks
like this. Theoretical big picture or opinionated questions
can go great on general q&a forums like Quora or specific
technical forums like specific subreddits or discord forums,
specific coding questions can go on these forums as well, but
will often get more eyes on coding forums like Stack
Overflow or Stack Exchange communities often the question
of Oh, should I post this on stack URL? flow or maybe a Stack
Exchange community is incredibly blurry. And sometimes it doesn't
really m
atter which one you post on. Now, if you run into a bug
or an issue with a technology you're really familiar with, and
you think it shouldn't be breaking, this is your chance to
pop an issue into their open source code repository and
potentially improve the tool. They don't have an open source
code repository, you throw that closed source piece of shit into
the garbage, but just kidding closed source Tech has its place
in our lives, too. Additionally, if you're following a tutorial,
and they hav
e a Git repo associated with it, like all of
my videos that do, that's gonna be the best place to leave your
issues. So as much as I hate to say it, putting your issues onto
my GitHub repositories is going to be much more effective for us
answering your questions than posting it in the YouTube
comments. Now finally, Discord, element, email, text message or
any other these unindexed chats are still good places to ask
questions, but please try to use them as a last resort. And if
they do end up an
swering one of your questions, maybe go back
and add that question and answer to one of the other forums that
we were talking about this way it will be indexed next time you
or somebody else, Google's it now these quicker chat forums
are places more for the community to congregate and have
quick conversations with each other. They're places to theory
craft, talk about new things coming out new ideas, events,
and other things that shouldn't be indexed by web crawlers.
They're also great places to
meet and network with people
that you might be able to bounce ideas off directly as you get to
know each other, which leads into our last section. But
before we do that, Oh, do you hear that? Oh, that's the video
inside another video alarm ringing. When you ask a question
in one of these forums, the better you format your
questions, the better chance you'll have of getting an
answered. Now there's no bad questions out there. But there
are poorly formatted questions. So let's teach you how to al
ways
ask questions as format as best as possible to give you the
highest chance of making sure they get answered. Number one,
before asking your question, make sure you followed all the
steps in the parent video. And you've done some research on
this already and make sure the question hasn't already been
asked. Number two, make a title that summarizes the specifics of
the question three introduced the problem before you write any
code, add minimalistic, reproducible code minimalistic
code means
it's not just a copy paste your entire file. If
you're having problems on one line, maybe just post that one
line reproducible code means that others should be able to
run to the exact same error that you're running into, or at least
post the steps for them to do it. This doesn't mean that you
should put I was following along Patrick's video and on our five
I ran into this problem, just watch his video and you'll get
there. As flattering as this is it's not reasonable that
everyone is going to h
ave watched my videos, even though
they should you want to give the technical steps to reach the
error that you've reached. For those of you watching my Free
Code Camp video, you're kind of exempt from this, but you can
only say, Hey, I was on our Five on this part of your video
inside of our discussions tab of the GitHub repo associated with
this course. So you can do that. But only in that GitHub repo
associated with this course, learning markdown to format your
code, especially using these th
ree backticks and labeling of
the language. This is a critical piece of formatting your code
and will drastically improve on the number of people who answer
your questions. Any errors or code should be formatted with
this three backticks syntax. And finally, often people who care
about certain technologies, monitor specific tags and
monitor specific questions being asked about the technologies
that they like. And then finally, again, be sure to read
the forum's guides before posting different fo
rums have
different rules about what they want and what they don't want.
So being familiar will increase your chances of getting an
answer. All right. So now back to the main video. Now a note
about Stack Overflow, in particular, Stack Overflow can
be a little aggressive, which is why sometimes posting on
specific community forums might be better for your specific
technology questions. If you post on Stack Overflow, and you
get a ton of down votes on your questions. Don't let that bother
you. Ju
st take it as a learning opportunity to learn about what
Stack Overflow likes and doesn't like and just keep going but do
not let that discourage you. Okay, well, now that we know
where things should go, where questions should go and how to
actually format them. Let's practice let's look at some
sample questions that you might have. And we'll figure out where
we want to put them. So the first one, where does this one
go? Feel free to pause and guess yourself. So a question like
this is going to
be great for a Reddit or a discord? Probably
more a discord. Now, this is definitely something that you
can search for. Right? So you probably could search for this,
find an answer and go from there. But maybe you want to ask
a buddy or maybe you want to ask a very specific community like
our slash eat Dev. Now, of course, if you see this
question, you obviously want to recommend Patrick Collins his
YouTube channel. Now how about this question. Notice its
formatting right? The title is nice and
big. They have a
technical command that is formatted properly. They have
Git commit which is formatted properly, where would this go
like this would definitely do very well on a Stack Overflow or
an index code based forum to very clearly try had to do
something technical. The problem is laid out very clearly. And
they've given the command that they're looking to do. Now, how
about this one, something like this could go on either
StackOverflow. But it's probably more likely going to go on a
GitHu
b issue for this brownie package. A big difference
between code forums and and Git repos like GitHub is that when
you make an issue on a GitHub repository, especially when you
think there's a problem, you do want to be as in depth as
possible. So oftentimes, when making an issue on these repos,
they'll even ask, what version are you using? Can you post all
your code? Can you post all your files and just be much, much
more explicit? So how about something like this. So this is
going to be really
good for the GitHub repo associated with this
tutorial, it looks like this person is asking about a very
specific tutorial. So posting this, there is going to be best.
Now if your question is on a tutorial that doesn't have a
GitHub repo, well, they probably should. But then maybe this is
better in the comment section. Now, again, this is where this
all becomes a little bit more art than science, because maybe
the specific error that they're running into is a generic error
that a ton of people r
un into, and maybe it is better on
StackOverflow. Or maybe there's an issue with the package. So
maybe it is better on GitHub. Or maybe the solution to this is
opinionated. And finally, what about this? Yep, this is going
to be much better for a discord or a DM with your buddy. And
away, it's our last step on blocking you from any question
is going to be join and strengthen the community of your
tool. Now at the start, it's going to be hard for you to give
back since you're not going to be very
knowledgeable on these
tools. But as you get better at these technologies, you'll want
to try to answer some of these new questions that do come in.
The reason is because this will give you a chance to actually
learn more about the tools that you like, it'll strengthen the
community of your favorite tools, meaning if you help
answer questions on tool, it'll actually encourage other people
to use the tool because there's a strong following there. And
likely, they might actually help you sometime
in the future, you
helping people will make you look like a good person. And
then you'll also feel like a good person. Additionally, in
many forums like Reddit, oftentimes, mods will actually
look at how often you post versus how often you help others
and comments on others people posts and some mods may actually
start blocking your posts for abusing the forums and not
giving back to the community and only trying to take knowledge
you in the community will be more successful if you join in
and h
elp others and not just try to extract things from other
people. Additionally, by engaging with the community, I
can't tell you how many people I've met and I've learned and
been able to brainstorm with. And then the final step is going
to be iterate through these steps. Maybe you get to the end
of these and you say, Oh, I'm still blocked, but you'll likely
be much, much more knowledgeable. So you want to go
back and try these steps again. Now this is where this whole
process is a little bit mor
e art than science. Because some
questions might not have been discovered yet. Only very few
people know not enough people understand the importance of the
questions, or maybe people don't understand your question. And
this is why it's important to go back and iterate on these steps.
Now that you have the basic building blocks of this
incredible superpower, I encourage all of you to go out
there and try this and then let me know how it went. Alright, awesome. So now that we
know more about how t
o get unblocked, we can move on. The
reason it's so important to learn how to get unblocked is
because blockchain and web three is more than just everybody on
their own. It is a very collaborative space. So as you
get better, and as you learn more a massive way to test how
much you've learned and give back to the community is to
going to Stack Overflow and going to stack exchange them and
trying to answer some questions yourself. So I highly recommend
you all go to Stack Overflow, and then you g
o to the GitHub
repo associated with the scores, you try to answer some
discussions, try to answer some issues and help other people out
because it's going to help you become a much better software
engineer. The other reason I want to do that part is because
when we install some of the tools that I'm about to show
you, sometimes the installation process is the hardest piece
there. Once you get past the install process, it generally
becomes much, much easier. But this can often be the hardest
par
t of the course is just installing some of these tools
that we're going to give you. And that's what we're going to
learn about right now. So we have been working so far with
remix, remix IDE, or integrated development environment. As
we've seen, it's this wonderful place where we can try out code
we can try solidity out, we can compile we can deploy, we can
pretty much do everything that we need to do. It's web based.
It can do testing, debugging, deploying local JavaScript VM,
it's very quick
and easy to create and test our smart
contracts. However, it does have some limitations. It can really
only deal with smart contract, it can't really integrate with
other parts of projects. It has limited support for tests or
custom deployments. And you need an internet connection to even
work with remix and it can be tricky to do a lot more advanced
functionality. So it's a phenomenal tool and absolutely
if you're looking to do something very quickly, I
absolutely recommend everybody just go to
remix to go ahead and
try something out how are now we're going to move over to a
more professional smart contract developer setup. And this is
with hard hat. This is known as a smart contract developer
framework similar to brownie or foundry or, and likes, there's a
number of these frameworks. And the reason that we're going to
do hard hat is because hard hat is JavaScript based. It's a
JavaScript based development environment. It's got JavaScript
based compilation, environment, deploying, tes
ting, debugging.
Now, for those of you who love TypeScript, we will also have
TypeScript editions of every single one of our code examples
for you. So if you love JavaScript, we got you, if you
love TypeScript, we also got you we're not always going to walk
through us doing the TypeScript. But we will sometimes, and all
of the code for the TypeScript will be available in the GitHub
repo. Now, before we can actually learn hard hat, we have
to learn another package first. So we're going to learn h
ow to
do everything with ethers. Jas, which is a JavaScript based
library for working with smart contracts. And it's also what
powers the next tool that we're going to be working with, which
is hard hat under the hood of hard hat. There's a lot of
ethers Jas. So it's important for us to learn ethers Jas so
that we can understand what hard hat is actually doing. Now for
the rest of the course, I'm going to be using a code editor
called Visual Studio code. This is one of the most powerful code
edi
tors on the planet. And if you've already got it set up,
feel free to go ahead and skip this part. If you already have a
professional coding setup with no GS and VS code, and Git and
everything, feel free to use the timestamps in the GitHub
repository. To skip over this setup section. You'll often hear
people refer to this as VS code, or Visual Studio code or just
Visual Studio. However, it's important to note that Visual
Studio code this is different than Visual Studio, which you
might see look
like this. So Visual Studio code is what you
want, not Visual Studio, Visual Studio is a different
application, make sure you're on Visual Studio code. Now, if you
choose so and you're a total Harto, you can absolutely work
just with your terminal, or just with PowerShell, or just with
whatever coding environment that you want, like atom or Sublime.
However, for us, we're going to be working with Visual Studio
code. And I'm going to be going through setting up Visual Studio
code the way that I
like to set it up, you can actually set it
up whatever way that you feel comfortable. And of course, in
our lesson six here, we have a link to installation and setup.
And I'm going to be adding more links as we go about here. And
once again, all the code that we're going to be working with
is in this GitHub repository down here where it says code.
Now we're gonna go through three different installation processes
and pick the one that's most appropriate for you. The first
one is going to be for M
ac and Linux users. The second one is
going to be for Windows users. And then our third one is going
to be a last ditch effort. If for whatever reason, you can't
get Windows or Linux or the Mac instructions to work, we're
going to use a Git pod installation. Now, I highly,
highly recommend that you try to get everything working locally
without using Git pod. However, if for whatever reason, you
can't get those installation pieces to work, we will have Git
pod instructions for all of the repos th
at we work with here.
But to get started, we'll start with the Mac and Linux
installation instructions. 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 goi
ng 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. Once you have Visual Studio code installed, the next
thing that we're going to want to install is going to be node j
s. And again, we have links to all of these in the GitHub repo
associated with this course, you can just go ahead and click
download for Mac OS or download for Linux, I recommend u
sing the
LTS version. lts stands for long term support, which means that
they will be supporting this version for a long time. So go
ahead and download Node js. I've already downloaded this, so I'm
not gonna go ahead and redownload this 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 where we can open up the terminal is we
can
go ahead and hit terminal and select new terminal and
you'll get something like this. Now you might have bash or Z ca
or some other type of shell type that you have doesn't really
matter because on Mac and Linux it's going to be Linux based we
can now test our no Jess installation has been done
correctly by running Node dash dash version, and you should see
something that looks like this. The exact version of node that
you have doesn't really matter here. But ideally you're at
least on Node vers
ion 14 or higher. And if something like
this doesn't show up, remember to go ahead and start looking at
Stack Overflow looking on the GitHub repo in the discussions
tab, looking on the updated section, etc. 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 Etherium, and the GitHub repo to
move past any issues you run into. Now, if you're on Mac or
Linux, you can actually hit Ctrl bac
k 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 no jazz is known as a
JavaScript runtime. And it's a tool that we're going to use to
help run JavaScript code in our Visual Studio Code slot exactly
JavaScript. And the difference between no Jas and JavaScript
can be
a little bit confusing, but don't let that stop you for
now. Next, we're actually going to go ahead and install 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 macOS, if you just type
git on the command line, it should go ahead and prompt you
to install it. So we're back in our command line, and we just
type git, it should prompt you to go ahead and stall it. And if
you do get dash d
ash version, you should get something that
looks like this. You can also use a Mac OS get installer by
clicking this link here and running through the installation
process. Alright, now that you have no JS yet in Visual Studio
code installed, we can continue on to the next section. Awesome.
If you're not planning on using Windows or get pod, feel free to
skip the next two sections. I'm running this on Windows 11.
However, it should work on most editions of Windows. So the
first thing that we're
going to want to install is Visual Studio
Code, which looks something like this, it should auto detect it.
And we're gonna go ahead and download this for windows in a
walk through all the installation process. Go ahead
and create a desktop item, we'll add this just in case we want to
open with code. And we'll go ahead and install. And then
we'll go ahead and finish. Once you've installed Visual Studio
code, you'll see something that looks a little like this, it'll
go ahead and give you this get
started with VS Code section
where you can choose some themes, and you can choose kind
of the way it looks feel free to customize it the way that you
want. If you want to learn a little bit more about Visual
Studio code, I highly recommend you walk through this section to
learn more about the shortcuts and making your development
experience more efficient. When you're done, you can just go
ahead and close the tabs at the top. And it'll look a little
something like this. Once we have Visual Studi
o code
installed. The next thing that we're going to want to install
is node j s. And of course, we have a link to installing this
in the GitHub repository associated with this course,
what we're going to do is we're going to go ahead and come to
node j s.org. To download this for Windows node. JS is a
JavaScript runtime environment, it's not exactly JavaScript. And
the distinction can be a little bit confusing, but just know
it's going to help us run our JavaScript code for our
development envi
ronment. Let's go ahead and download the LTS or
the long term support edition of no GS. We'll go ahead and we'll
run through the setup wizard. And we'll go ahead and make sure
that this is clicked just in case we ever want to use some
different tools with our setup. And then we'll go ahead and
click Install, you'll get a pop up asking if you really want to
install this on your device. And we'll go ahead and hit yes. And
then we'll hit finish, you might get a pop up that looks like
this, go ahead
and click any button to go ahead and install
the tools, go ahead and press any key again. And you'll likely
get a Windows PowerShell screen pop up asking you to go ahead
and install a whole bunch of different projects and files. If
you chose not to install this, that's totally okay. But in the
future, this will be really helpful since we are going to
use a lot of tools that this package installs anyways, this
might take some time to install. So go ahead and be patient. And
if it gets stuck, it
might just be waiting for you to go ahead
and hit enter. So go ahead and hit Enter for any prompts. But
yes, please be patient with this, it can be a little bit
slow to go ahead and install everything. Once you have all
that installed, you can come back to Visual Studio code. And
we're going to go ahead and open up a terminal. To do that we hit
terminal and we hit new terminal terminal is going to be our
command line prompt where we're going to basically run all of
our scripts to work with our c
ode. And if you run Node dash
dash version, you should see something that looks like this
to know that you've installed Node js correctly. Now this
command line is known as Windows PowerShell. If you want to work
with Windows PowerShell, you absolutely can. In fact, if you
want to be a total Harto, and write all your code through
Windows PowerShell, you can absolutely do that as well.
However, we're actually not going to be working with
PowerShell We're going to be using a tool that makes our
Wi
ndows environments more like Linux. The reason that we're
doing this is Linux is the standard for most development
environments. And having everybody work on a very similar
setup will make the rest of this course a lot easier for everyone
to interact with each other, no matter what coding environment,
they're working on. WsL stands for Windows subsystem for Linux,
and allows Linux programs to run natively on our Windows
environments. To get this setup, we're gonna go ahead, go to the
WsL install
. And we'll have a link for this as well in our
GitHub repository. So what we're going to do, you must be running
a Windows 10, version 2004 or higher, or Windows 11. If you're
using an older version of Windows, you can absolutely
continue with PowerShell. But you might run into some issues
where all the commands don't work exactly the same. So I
highly recommend working on a newer version of Windows. To
install this back in your Visual Studio Code, PowerShell, or just
the PowerShell app, or run
ning WsL dash dash install. Now if
you get this error, the requested operation requires
elevation, it means that we have to run our Visual Studio code or
our PowerShell application as administrator. We close out our
Visual Studio code. And right click it and say, Run as
Administrator, we get a little pop up saying Do you want to
allow this app to make changes to your device, we'll go ahead
and click Yes. And then we'll reopen the terminal. And then we
can run WsL space dash dash install. And we'
ll go ahead and
install WsL. This may take a little bit so please be patient. Once it's completed, you'll see
something that looks like this, we're going to be working with
Ubuntu. And we have a list of different commands to change
your Linux distribution. If you choose to do so. And you'll see
the request set operation is successful changes will not be
effective until the system is rebooted. So you'll want to go
ahead and restart your computer. There's also a troubleshooting
guide in the GitHub
repository. If you run into issues after you
restart your computer, you'll be prompted for a name for your new
system and a password. This can be different from your Windows
name and password, and then just follow through with all the
prompts. And then once you're done, you'll be dropped into a
Ubuntu shell, and you can run Linux commands. Now you'll have
a Ubuntu instance, on your machine. And you'll be able to
run Linux commands in your terminal. Now that we have WsL
set up correctly, we're g
oing to head back over to Visual Studio
code. Once we have WsL, we'll want to go ahead and install an
extension. So in our Visual Studio code, we'll go to
Extensions, and we'll look up remote development you can
install the whole thing or just remote WsL we're gonna install
the whole thing you can follow along to get started with remote
WsL if you like, or you can just follow along with me right now.
Right now if I go to the terminal and hit new terminal,
I'm still gonna get PowerShell we actual
ly want this to be our
Linux shell. So there are a couple ways to open Visual
Studio code up in our Linux environment. One way is you're
gonna hit Ctrl, Shift P and type WsL. And then click remote WsL
new WsL window, and you'll get a new window that looks like this,
you can also click the bottom left and choose New WsL window.
Now if we go to our terminal and hit new terminal, we'll be
dropped into a bash shell and we can run Linux commands in here.
Awesome. Another way we can open up Visual Stu
dio code with WsL
on is we can go into our your boon to application, we can make
a folder by typing MK dir folder, we'll CD or change
directory into folder and type code period. We'll go ahead and
trust the authors in here. And this will open up Visual Studio
code already connected to the folder that we're in. And we can
create files in this folder like hi dot txt, and those will get
created inside the folder in our WsL. Now that we're inside our
WsL environment, you'll notice that node dash das
h version
doesn't work anymore. That's because we installed it on our
regular Windows machine and not on our WsL or Linux environment.
The reason we installed it on a Windows environment first is
that just in case you wanted to go ahead and use PowerShell or
user Windows environment to run everything you still can just
remember if you use the Windows environment and PowerShell all
the commands that we use might not work for you. So we're gonna
go ahead and install Node js on our Linux environmen
t. Now, all
the commands that we're going to write are going to be in the
GitHub repo associated with this course. We're going to do a curl
command to the nvm repository, which stands for node version
manager. And we're going to pipe the
install script into bash. And this will go ahead and install
and VM. After running that, go ahead and trash the terminal,
and then reopen it. And then we can type MBM dash dash version
to see if it actually installed correctly. Once we've installed
nvm, we can g
o ahead and install no GS using nvm. Just type npm
install, and we'll do 16 point 14.2 Is that same long term
support version we installed on our Windows machine. Once we
have it installed, we can type in Node dash S version. And now
we have no JS installed. Now I know there was a lot here, but
if you made it this far, this is fantastic. You've done a great
job to push through to this point and get everything set up
so you can code in the best environment there is. So if
you've made it this far,
huge congratulations. Now the next
thing that we're going to install is Git. And now we're
not going to use Git for this lesson. However, we will
definitely be using it in the future. See if gets installed
type git dash dash version, sometimes Linux will
automatically come with Git installed and you'll see
something output like this. If you don't have Git, we can just
look up the get install. Which again, link to this is in the
description. And you'll actually run the installing on Linux. Now
a
gain, now if you want to use PowerShell, and you want to do
everything with Windows, you can absolutely follow the installing
on Windows instructions here instead, when you're working in
WsL, you'll want to use the Linux commands instead of the
windows commands even though you're on a Windows because WsL
makes it so that you're basically running in a Linux
environment. And now if you've made it this far, you should be
able to follow along with the Mac and Linux instructions as if
you're running
on a Mac and Linux even though you're running
on Windows. Just be sure that whenever you're in your VS code,
you take a look at the bottom left and make sure you're on WsL
Ubuntu. Like I said before, if you want to run in PowerShell,
or in a Windows environment, you're more than free to do so.
But like I said, if you've made it this far, huge
congratulations. Awesome work. And then finally, our last setup
is going to be using a tool called Git pod starting from
Lesson five. The lesson that we're
on right now, ethers,
jazz, simple storage, all of our code repos are going to come
with a button in the repo scroll down, they'll come with this
open in get pod button. Now git pod is a cloud development
environment where you can actually run your code on a
remote server, it's kind of similar to remix IDE, but it
allows you to run Visual Studio code in the browser or connected
to another server. This is good because then you don't have to
do any installation on anything. Since all the tools th
at you can
want to use are just going to be running on this remote server.
This has its downsides, though, obviously, since you'll only be
able to code if git pod is up and working for you.
Additionally, when it comes to private keys, you absolutely do
not want to run any code with a private key that has real money
in good pop. Why? Well, once again, since you're running your
scripts on a remote server, those servers have access to
your private keys. But since you've Pinky promise that for
this
course, you're not going to be using a meta mask or a
private key with actual money in it, it should be fine. The other
downside is that these often cost money to use and get pot
isn't free. But it's an option if you absolutely cannot get any
of the installation working. So if you go ahead and you hit this
opening get pod button, you'll get a welcome to get pods
showing up. We're gonna go ahead and continue with GitHub. Since
you've signed up for GitHub here, you want to go ahead and
authorize g
et pod. And it'll go ahead and start creating this
workspace for you. And you'll notice it looks exactly like
Visual Studio Code. Since I opened the repo up in Git pod,
it came with all the code, and you can even open this workspace
up in VS code desktop. So this is might be a little bit
confusing. But basically, you can run off of Git pod using
your local Visual Studio code. And if you see git pod here,
that's how you know that you're running off of Git pod. If you
see this pop up, do you want
to open this workspace in VS code
desktop, you can hit Open. And it'll ask you if you want to
open up Visual Studio Code, which I'm going to go ahead and
hit yes. And you'll get something that looks like this
on your Visual Studio Code. It'll tell you that it wants to
install the Git pod extension, and then open that Git pod URL.
So you can go ahead and install it. Reload window and open and
it's going to go ahead and start connecting to our the Git pod
workspace. And this is going to be the sam
e as running git pod
in the browser here. Or you can also do it manually by hitting
the Git pod in the bottom left, and then type in open in VS code And then you should be able to
run it in your Visual Studio code. For now, I'm going to
recommend that if you're using Git pod, just stay in the
browser, just so that you know, okay, I am running this on a
remote server. And just as a reminder for you that you're not
actually locally developing. And hopefully, this will be a
trigger to not actually
put any special private keys or anything
like that. But you can make workspaces, you can make new
folders, and you should be able to run all the commands on here,
as if you are running locally with Visual Studio code. To open
up the terminal, you can hit this little bar at the top left,
go to terminal, new terminal, or use CTRL tilde exact same as Mac
OS and Linux keyboard shortcuts to create a new folder, we can
change directory, CD, dot.mk, dir, new folder, MK dir makes
make directory called N
ew Folder. And then we're going to
change our directory into a new folder, and hit enter. And now
we're in that new folder. For each section, you can either
open up the entire source code right into GitHub, or you can
create a new folder for each section yourself and start from
blank. And then you would just type code period. And you'd be
in a brand new folder. All right, this is fantastic. At
this point, you should be set up with Visual Studio Code no Jas
and get. And I'm going to be working ou
t of a folder called
Hard Hat Free Code Camp at this point, you should have node,
dash dash version, get dash dash version. And if you're using
Windows, this should say WsL or your boon to or something like
that. And if you have all that, that means we're ready to go.
Now a quick note something that you'll see me do a lot. And you
can do this as well. Oftentimes, when my terminal gets really,
really big, or there's a ton of commands in here gets a little
bit overwhelming for me. So one thing tha
t you can do is you can
type clear, and hit Enter to clear it. Or what you can do is
you can hit Command K, if you're on a Mac, or Ctrl K, if you're
on a Linux or a Windows, and it's one of my favorite keyboard
shortcuts that I use all the time. Additionally, the trash
can and the X here are very different. If I go to a couple
of enters here, and we're down here, if I hit the trashcan, and
then pull my terminal back up, by doing the toggle, or by doing
terminal new terminal, you'll see all those
lines are still
here. But if I hit the trashcan, and then pull the terminal back
up, you'll see it actually refreshes mine a special command
that prints stuff out. trashing your terminal is basically
deleting whatever's running in it, and the x is just hiding it.
And as hitting Ctrl tilde or toggling our terminal or
whatever command it is on your environment, that's equivalent
to hitting the hide, not the trash. So if we want to remove
and start a terminal over, we hit the trashcan, and then we
pull it back up. Alright, so now we're gonna start working with
ethers. And we're going to start learning to code, our
transactions and our contract deployments and everything
programmatically at a relatively low level. And we're gonna learn
how to deploy and interact with the contracts using the ethers
JS package. Now to get started, I'm going to recommend you
create a folder where you're going to put all of your
projects in it, I'm going to create a new directory called H
H. hyphen, FCC, whic
h stands for hard hat Free Code Camp. And
once we run that command, we can cd into hh FCC. And this is
where we'll create all of our projects for this course moving
forward, so that we have them all in one place. Now to get
started, whenever you create a new project, you always want to
create a new folder. So to create a new folder, we're going
to do MK dir, and we're going to call this ethers, simple
storage, like that. And now, if you type ls, you'll see that
there is one folder named ethers,
simple storage. ls is
how you list all the contents of your folder. You might have a
lot of other folders in here, I only have the one since I
created this new folder for this. Now what you can do, you
can type code, ethers simple. And then if you hit tab, it
should autocomplete for you. And if you enter, Visual Studio code
should open up a new Visual Studio code for you. That is
inside of either simple storage if you open up your terminal
now, your home directory for this workspace is going to
be
through simple storage as this is what pops up. If that doesn't
work for you, we can also do is you can hit File, Open folder
and then open the folder that you just created or that you
want to open this again we'll open up VS code. And if we open
up our terminal we see we're inside of ether simple storage.
This is so powerful because as we create files we'll be able to See those files
in our Explorer here, this button here stands for the
Explorer. If we click it, we can see the different file
s in here.
And I'm going to go ahead and actually delete this file dot
txt, because we're not actually going to use that. Now it's this
part of the course, where we're actually going to start jumping
into some JavaScript. Since this course is in JavaScript course,
if you're unfamiliar with JavaScript, it might be a little
bit tricky. If you want to come into this with a better
understanding of JavaScript and Node js, there is a Free Code
Camp YouTube video teaching No, Jas for beginners. And a l
ink to
this will be in the GitHub repo associated with this course.
There's also a JavaScript Free Code Camp video that I'm also
going to put in the description for this course, keep in mind
that JavaScript and Node js are slightly different. And we are
going to go over some of the differences as we code along
here. But for the most part, learning one means you've
learned the majority of the other. So if you want to pause
and go through these videos, before continuing here, please
feel free to d
o so you don't have to, you can absolutely
continue on with the course as is. And if you get confused or
stuck on some JavaScript piece, feel free to pause, Google it
and come back. But just to reiterate, you can check out the
JavaScript programming full course. And also the Node js
full course, as those are both going to help you. Like I was
saying before, Node js is a JavaScript runtime. So it's not
exactly JavaScript. But we're going to write our code in
JavaScript. And if that's confusing, j
ust don't worry
about it right now. And as we go through this course, I'll show
you where the differences are. But basically, you can think of
no Jas and JavaScript kind of being the same thing. The big
thing about Node js is it allows us to write JavaScript code in
the back end, as opposed to running JavaScript on the front
end, JavaScript is made to be a browser run language, like
running inside of, you know, Chrome, brave Firefox, etc. No,
Jas allows it to become a a scripting language, a bac
k end
language, which is why the syntax between the front end
JavaScript and the back end JavaScript or the no JS
JavaScript are going to be a little bit different.
Additionally, as we go along in this code, if you're familiar
with TypeScript, all of our code is going to come with a
TypeScript edition. TypeScript is what's known as a statically
typed version of JavaScript. And it'll be it'll be this one.
It'll be TypeScript, not TypeScript edition. I'll go
ahead and fix that and remove this one
to make it clear.
TypeScript is a type safe version of JavaScript, which is
that's confusing, don't worry too much about that. But we are
going to do all of our programming in JavaScript. And
then if the code is different enough, I'll show you how to do
it in TypeScript as well. However, for most of them, we're
not going to show you the TypeScript editions, because
it's going to be really similar. But you can always refer back to
the GitHub repository to see all the code for the TypeScript.
Now,
if you're new to this space, I actually do recommend
you go ahead and start with JavaScript and learn how to do
TypeScript later on. TypeScript actually catches bugs early on,
making it a lot easier to code your projects in the long run.
However, it does take a lot of extra typing. And it can be a
little bit frustrating learning how types work for beginners,
JavaScript is a little bit more loose as a language and lets us
kind of do whatever we want, but it can cause a headache later
on. So if y
ou do run into some issues, and you do run into some
bugs, it might be a good idea to try TypeScript on for size, and
see how that fares. Well. Let's go ahead and begin working on
our local development environment and getting set up
to do everything in ethers Jas and in JavaScript. Oops. And I
actually went and renamed this folder to ether symbol storage
dash, FCC. Reason I added this dash FCC is all the GitHub
repositories associated with this course, all the GitHub
repos that have this dash FC
C to know that it is part of this
hard hat JavaScript course. Awesome. Let's jump in. Let's
start working with solidity and our smart contracts locally in
Visual Studio code, as you can probably tell by the name of
this folder. And of course, if you looked at the code, this
project is going to be our simple storage project, but
developed locally using ethers. So the first thing that we want
to do is want to get in that smart contract code. So what we
can do is make sure we have the Explorer sele
cted, we can go
ahead and right click and select new file and do simple storage.
So just close this for now. And we can copy paste our simple
storage code from our last section into VS code. If you closed remix or you
forgot where it is, you can just go to the ethers simple storage
FCC repo, it's simple storage dot Sol, and we can just copy
all the code in here and then come back over select Symbol
storage that Saul and paste it in. Now an important note about
Visual Studio code is that when you
see this little white.up
here, it means that this file isn't saved to save your Git
file. To save, and it'll go away like that. Or what you can do is
you can hit command S or Ctrl S, depending on if you're on a
Windows, Mac, or Linux. Now you'll see here that this code
is a little bit hard to read the simple storage dot salt, it
doesn't have the syntax highlighting that we saw in
remix. So we want to go ahead and add a Visual Studio Code
extension to give this syntax highlighting. So what we ca
n do
is come over to this bar over here looks like this. And if you
don't see it on the left hand bar, you should click these
three dots, and it should be in here. But we'll go ahead and
click Extensions. And what we're going to do is we're going to
look up, solidity plus hard hat. And we're going to install this
solidity plus hard hat extension. For VS code. Now that
we have this installed, if we go back over to our simple storage
dot sole, you'll see that all the highlighting is back in. And
n
ow it's much easier to read. Right? So this is good. We have
our code in here we have our syntax highlighting. Now let's
add an auto format or or default format or right now our code is
pretty good with the way that it's formatted. But what if we,
we accidentally do some stuff like this or like this, or maybe
even like this, we add a ton of new lines, etc, our code can
start to look pretty gross. And even though the code itself is
fine, and it'll run the exact same way, with all this extra
white
space, it doesn't look very good, right. And due to that, it
can be a little bit hard to read. What we want to do, then,
we want to open up our settings and adjust our vias code so that
it auto formats whenever we save, so whenever we save, so
that whenever it goes from the little white dot here to no
white dot, this whole thing gets automatically formatted to some
looks really nice. Okay. So what we can do is we can open up our
command palette to open up our command palette, you can hit
View co
mmand palette, and we'll get a little pop up that looks
like this. Another way to open up your command palette is
you're gonna hit Command, Shift P, or Ctrl, Shift P, depending
on your Mac, or Linux or Windows. And what we want to do
is we want to type in settings, and we're going to open Settings
JSON, we don't want to open the default settings JSON. We don't
want to touch these. But we want to open our JSON settings.
You'll also notice there are user settings and workspace
settings. These are
pieces that we can adjust as well. But we're
going to just go right into the JSON settings. So So I already
have some stuff in here, but yours might be blank. Or you
might have some stuff in here as well. So what we're going to do
is if you have stuff in here already, we're going to add a
comma, then we're going to do quotes, solidity. Close the
brackets, we're gonna do a little colon and something like
this. This means that we're going to apply some settings to
our Visual Studio Code. Whenever
we're working with solidity, one
of the things we're going to add in here is going to be an
editor, Doc's default, for matter. And you might even get a
pop up that tells you some different things that we can use
for a default format or our default format, or is going to
be Nanak, foundation, dot hard hat, hyphens solidity. This will
mean that any time we go to format our code, it'll use the
hard hat solidity plug in, as its default format, or the hard
hat solidity plugin comes with some formatti
ng, and a lot of
other really useful tools for us writing our code. So now that we
have this part in, the next thing we're going to do is add
format on Save. If we haven't already, we could add it in here
in our JSON, but I'm going to add it not in the JSON file, I'm
going to add it in the overall file. So if we open that command
palette back up, and we type in settings, open user settings,
this is another really good place where we can look in add
settings with the UI. So these two do essential
ly the same
thing. It's just that this one has dials and a little bit more
context versus settings that JSON just says Okay, give me the
raw code for it. This tells us a little bit more. So you can use
either one. But we're going to look for format, on save. And
you're going to want this checked if you haven't already.
This means that every time we save VS code is going to try to
format our code for us. So now that we have this checked, we
have settings dot JSON added in. And remember, we want t
o save
this. Remember, if you see this white dot, that means not saved.
So you're gonna want to save it and close it out. Let me close
this out too. If we come back in here and we add a bunch of
random new spaces or whatever, you want to make it look a
little bit ugly, and then we save it. It should automatically
reformat to look much nicer, right. So if we do something
like this, we'd save it reformats it to look much nicer
this one make them more readable for you, and more readable for
anybody
else who looks at your code. And it's just really nice.
And this line of code for your settings dot JSON is located in
the full blockchain study course.js. So you can also just
copy paste it, we are going to end up overriding that default
format or with another format are called prettier pretty soon.
But it's great to have a default format or so that if you don't
feel like adding the prettier code sometime in the future, you
can just rely on your default formatter. Awesome. While we're
doing fo
rmatting, let's also add a default format or for our
JavaScript code. And just to test it out, let's go ahead and
create our new file, we'll call it deploy dot Jas. And in here,
we can do something like function Hi, console dot log, hi, and then
just add and then just make it look kind of gross, maybe
something like this. If you hit save, and it does some auto
formatting, that's great, you can actually turn that off by
going back to your command palette, and saying, Save
without formatting. And
that way it will be saved and not
formatted. The way we can add some other formatting here is
we're gonna install another extension, this one is going to
be called prettier. So we can just look up prettier and
extensions here. And you'll want to install this prettier code
formatter. So we're gonna go ahead and install this. And
great, now it's installed prettier is a form is a code
format, or that works for many languages like Python,
JavaScript, and even solidity. And pretty soon we're going to
use prettier for both JavaScript and solidity. But for now, we're
just going to use prettier for JavaScript, we can enable this
by opening back up our command palette, we'll go to
preferences, open Settings. And the same way we added a solidity
section, we're going to add a JavaScript section. So we're
going to add a comma here, some quotes, brackets, we're going to
type in JavaScript, close that colon brackets. And we're going
to do the same thing at the door dot default, or matter. Net
we're
going to do and then in here, we're going to do e s
BENP, that prettier, hyphen VS code. And this will make
prettier the default editor for JavaScript. Now, like I said,
pretty soon, we're going to have prettier override both of these
for solidity and JavaScript. And we're going to give prettier
some parameters so that no matter who uses your code, they
will always have the exact same formatting. But now that we have
prettier in here as the default editor, if we come back to
deploy dot j s, and
we hit save, it should format to look a
little something like this. Now, we go ahead and we do something
like this. If we go ahead and do something like this, it'll
reformat to look like look like that. And then additionally, if
you want to go back to the command palette, open user
settings, not in JSON mode, and we go to default format, or you
can actually even select the default format or for all
languages, if you want to use prettier for all formatters.
Feel free to go ahead. Alright, great,
got our JavaScript
format, or in as well, let's start writing some JavaScript
code. Awesome. So now that we have our
code in here, it's time for us to learn how to actually deploy
our contract using JavaScript. This is going to teach us a lot
about transactions and what's going on under the hood, and
even what's going on under the hood in remix, and remix, we
usually just hit a compile button, and then hit a Deploy
button. And that's really it. In JavaScript, we're actually going
to create our o
wn functions that are going to help us do both of
those. And as I mentioned, this is the part of the course we're
going to start working with JavaScript, and optionally,
TypeScript if you like. Like I said, all of these sections come
with a TypeScript edition as well. So let's set up our
deployed out js script to actually deploy our simple
storage dot soul. So how can we get started here? Well, let's
first learn how to run a script using JavaScript and Node J S.
So if you want to do a print line
, or just print something
out to your terminal, we can do something called a console dot
log. And if I do console dot log, hi, I hit Save. And I open
up my terminal. I can now run Node deploy.js. And it'll print
out high node is how we say hey, we want to run this JavaScript
code using Node js. Other languages you might be familiar
with, sometimes we'll do Python, you know, deployed up py, or
Java, C deploy dot Java, you know, etc. But with JavaScript,
since the front end and back end JavaScript
are different. We run
code on the back end with node. And with JavaScript, you can do
a lot of things that you'd see in something like solidity with
a little bit looser have a structure. If I wanted to create
some variable, I could say let variable equals five, this kind
of similar into solidity like you and 256, variable equals
five. But in JavaScript, we use let or var or const. And then I
can print this out, I can do console dot log variable. And I
had saved JavaScript is optional on whether
or not you actually
want to have semicolons. Here, I think prettier, defaults to
putting semicolons. And we'll get rid of those in the near
future in your terminal. Once you start typing the name of a
file, if you hit tab, it could autocomplete the rest of the
file name for you node deploy hit Tab, it'll autocomplete and
if we hit enter, now, we get Hi, and then we get five. Since this
prints out Hi, and then it prints out five. JavaScript
automatically starts with whatever code you have at the
top of the file. So it does console dot log first and
variable. And then this one as well. However, a good practice
is to actually wrap everything you want to do in a main
function, and then run that main function. But what we could do
is up at the top here, we'd say function, mean, add parentheses
and some brackets, a closing bracket at the bottom, and hit
save for its auto format. Right now, if we run this, nothing
will happen, because we've wrapped all of our code in a
main function. So if I
run it now node deployed to Jess,
nothing happens, because I need something to call the main
function. So if I then take this, this main function down
here, and I call main, our jobs from code is actually going to
say, ah, the first line of the script is actually this main
function here. So now if I do node deploy dot j, s, it'll run
high and five, which is what we want. Now, this is going to be
the setup for most of our scripts moving forward,
including the scripts that we write, when we get t
o hard hat,
however, there is going to be one major difference. Instead of
regular functions, we're actually going to use something
called async functions. And we're going to do something
called asynchronous programming to do this. Now, if what I say
next is really confusing for you don't let it slow you down, feel
free to go ahead and watch that JavaScript course to learn more
about this. But I'll also let you know, hey, this is an async
function. Here's what we need to be aware of when working
with
it, okay. But I do want to give you
a quick bit 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
synchronous, Synchronous means it just goes one line after
another. If our code looks like this, this is synchronous write,
our main function is the first thing that actually gets called.
Then we do console dot log, that variable equals five, and then
console dot log. Again, this is s
ynchronous 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. A 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 for popcorn
to finish. And then, 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, we're 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 pour
the drinks right away and then wait for the popcorn to finish.
So asynchronous programming is way for us to do stuff without
waiting around for things to finish. And this is really
advantageous in JavaScript by default allows us to do this
asynchronous progr
amming, 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 placed salt on popcorn. Of course, if we want to play 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 No, you're good. You can go on and
do another ta
sk. 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 problem. This, 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, putti
ng 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 th
is 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 pseudocode to pretend what this code would look like if
this was actually a function. So we 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 pop
corn function. So we'll say,
okay, cook popcorn, then the next thing we're gonna have to
do is we're gonna have to pour drinks, or drink. 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 cook popcorn is going to be a function where we could say
let status equals cook popcorn. And while the popcorn is being
cooked, the status is going to be ending. 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 pro
mises things. So we need to tell our code, hey, you
have to wait. Or you have to wait for cook popcorn and for
poor drinks to finish. So what we can do now without getting
too deep into the weeds on how all 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 prom
ise 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 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 now Ah, okay,
the function that's being called is promise base
d. 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 hav
e
to wait for it to be deployed. If we don't use synchronous
programming, and we just leave our function main like this,
what would happen is we would write some code like contract
dot 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 it, 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
contrac
ts 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 either wait for our popcorn to finish or
continue on. So now that we have our main function as an async
function, we're going to add some code to our main function
down here. And the code that's added here is some syntax for
working with asynchronous functions. And if this part is
confusing, I'm just going to say for now, absolute, don't worry
about this. If you
want to try to understand that later. That's
fine. But for those who are following along, we're just
going to add an add then catch air air 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 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 clump 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 gonna delete all this for now. But you can still find
that common in the GitHub repo associated with this course.
Awesome. Okay, so now that we have our real basic setup, let's
go ahead and s
tart coding. And if this set apart is confusing,
and the async await stuff is confusing, don't worry too much
about it. It'll make more sense as we progress. All of our code
basically is going to be inside of this async function main,
which is going to be our main script for deploying our simple
storage dot soul store deploy script is going to replicate
exactly what goes on in remix, in remix, what was the first
thing that we always did? Well, the first thing that we would do
is actually compile
all of our code. So we're going to want to
compile our code in our JavaScript project as well. In
order for us to compile our simple storage contract, we're
going to use a tool called Silk Jas, and JavaScript actually has
a way to install projects directly into our setups and
into our folders. If we scroll down this silk Jas is exactly
what we're looking for. Because it has a way to compile a
contract that imports other contracts via relative paths,
you can see a section in this readme and most
documentation
will have something like this, if it's JavaScript compatible.
For Node js usage, it says to use the latest stable version of
the solidity compiler via Node js, you can install it via NPM
npm install. So NPM is what's known as a package manager. And
we actually installed NPM just by installing no Jess, right, if
we do no dash dash version, you should also be able to do NPM
dash dash version. Another tool that it comes with is something
called core pack. And you can type core pack d
ash dash
version, now we can install with NPM using npm install silk,
however, I like the yarn package manager a little bit better. So
we're actually going to install the yarn package manager instead
to do all of our package management. If you go to the
installation page, the newer way to install yarn is just by
running core pack enable. And the older way is to install with
NPM. If you go ahead and run core pack enable after that
finishes, you should be able to run yarn dash dash version.
Altern
atively, you can just run NPM i dash g core pack, and then
you can run core pack enable. The last option you have is you
can run NPM i dash g yarn. This will install yarn
globally for your system. But this is considered the outdated
way to install yarn. And ideally you run core pack enable. But in
any case, after you run those, if you run yarn dash dash
version, you should get something that looks like this.
Now that we have yarn, we can actually use yarn to install all
of our projects, instead
of NPM. Back in silk Jas where it says
npm install soak, we can do the yarn equivalents of NPM install
sock, which is going to be yarn, add sock, this will actually go
ahead and install solc to our project. If we open up our
folders, you'll actually see that this added a couple of
different folders, it first added a package dot JSON package
dot JSON is a file that tells us a lot about our project and the
dependencies that it works with. For example, we've installed the
soap package of 0.8 point
13. Our yarn dot lock tells us the
exact version of all the different packages of our
project. For example, the reason this is so important too, is
solc has a ton of dependencies as well. So yarn dot lock tells
us exactly what version of solc and all the different
dependencies of solchen. Any other project that we add. This
is autogenerated file, don't edit the file directly. The
final bit is we got this node modules folder. This node
modules folder is going to be where all the installed code
th
at we just downloaded is, for example, if we look at node
modules, we can see there's a sock folder. And inside this
sock folder is all the code associated with this soap
package that we just installed. And since we're working with
zero point 8.7 of slowly, we actually want to install that
specific version. So we're going to do yarn add solc at zero
point 8.7 Dash fixed and you'll see in our package dot JSON you
can now see zero point 8.7 Dash fixed in our dependencies
section for sulk. Normally
you can just add your salt version
like yarn add zero point 8.7 But there was an issue with zero
point 8.7 So we had to do zero point 8.7 Dash fixed. You can find the different
releases and the different versions if you go to sulk Jas
releases and then to tags Alright, great. Now that we have
sold, we have the ability to actually compile our contracts,
we could either compile them in our code and have it
automatically run whenever we hit Deploy, or, or we could
compile them separately. If you w
ant to go back after this
section is an example in the sculpt Jas repository that shows
you how to actually compile silk right in your code. We are
actually going to compile them separately using a soap Jas
command. The yarn command is both used to install
dependencies and it can be used to run scripts. If you go to
sulk Jas and you scroll down, it says in order to use the command
line, you actually need to install it globally. If you want
to install this globally using yarn Global Add solc at z
ero
point 8.7 Dash fixed you can absolutely go ahead. However,
since we're inside of this folder here, which has the yarn
dot lock the package json and the node modules yarn will be
smart enough to know i You're looking for the soak in this
folder. So we can actually go ahead and compile our contract
using yarn and solc Jess if you want to see all the different
commands soak Jas allows we can just run yarn. So Jas dash dash
help, and you'll see it'll spit out a list of all the different
options
that soft Jas has. You can also run yarn, so Jas dash
dash version to just make sure that we're on the correct
version, which is indeed zero point 8.7. Now to actually
compile our simple storage at soul, let's run the compliation
command we will run yarn, so J S, dash dash bin since we want
the binary dash dash ABI since we also want the ABI dash dash
include path node modules. Since we want to include any contracts
or files in our node modules, we aren't using any for this
project, but in the f
uture, you will need to include this dash
dash include path node modules do dash dash base path of
period, this period means that the base path is going to be
this folder dash O period, which means we're going to output the
compiled binary and ABI to this folder. And then finally, simple
storage dot soul. Since this is the contract that we want to
compile, auto completed it from simple storage to simple
storage. So by typing simple, and then hitting Tab, but let's
go ahead and hit enter, you'll
see it's running this command to
compile this contract. And you'll see two files get output.
One called Simple Storage soul underscore simple storage that
API in simple storage Sol underscore simple storage dot
bin, the ABI is obviously the ABI of this contract, which will
need in the future. And then the bin is going to be the binary,
or the really low level of this code back and remix. If you
compile simple storage, that soul, you can actually look at
compliation details, like the ABI which we
just got, or the
bytecode, which if you look at this object 60806 That's the
same as the binary here 60806. Alright, great. So now we've
compiled our contracts here. Now, obviously, now if you hit
up on your keyboard, you can actually cycle through your most
recent terminal commands. And if you hit up enough, we can see
this command that we just ran typing this out or hitting up a
whole bunch every single time, it's going to be really annoying
to do anytime we want to recompile. So what we can
do is
we can add a script section in our package dot JSON to shorten
some yarn scripts for us. So what we'll do is back in our
package, dot JSON, a lot of comma, and we'll add a section
called scripts, add the colon and the brackets. And in here,
we'll say anytime we say compile, we will run this long
command. So I'd compile we'll put some quotes. And we'll paste
that in there. So now instead of typing that whole thing out, as
long as we're in the same folder that our package json is in, we
can
run yarn compile. And this will run that whole script for
us without us having to type the whole thing out scripts are a
really useful way to make it easier for us to run long
commands. All right, great. Now we have our code compiled, this
is going to be equivalent to US hitting this compile button for
us to actually go ahead and deploy. So now we have our simple
storage. That's all compiled, let's learn how we can actually
deploy this thing. Remember, in remix, we actually deployed it
to one of
two different places, we deployed it to either a
JavaScript VM, or with our injected web three, with our
Metamask. Let's learn first ad, do the JavaScript VM. And then
we'll learn how to use the injected web three, or a meta
mask or some connection to an actual test net. So in order to
deploy this to a JavaScript virtual invite I'm in or kind of
a fake blockchain, we're gonna need to get a fake blockchain.
Now in the future, we're going to be using the Hardhead runtime
environment as our JavaSc
ript virtual machine or aka our fake
blockchain. But for this section, I want us to use a tool
called ganache. There's a link to this in the GitHub
repository. Ganache is similar to a virtual machine in remix.
It's a fake blockchain that we can run locally to actually
test, deploy and run code. It's also a nice way to see things
that are going on in a blockchain. Let's go ahead and
spin up the ganache application after you install it, the
ganache application will look a little something like thi
s. And
to spin up a fake blockchain really quickly, you can just go
ahead and click Quickstart. This will spin up a fake blockchain
that's running locally on your computer right here, which is
fantastic. It comes with a whole bunch of fake accounts exactly
like how remix comes with a ton of fake accounts with 100 ether
each ganache comes with a bunch of fake accounts with 100 ether
each. They also come with the private keys that we can use in
our applications to actually take control of these fa
ke
accounts. Remember, don't use these private keys on a public
blockchain. They're for development purposes only
because a lot of people know these private keys in our code.
One of the first things that we're going to need to do is
actually connect to our blockchain remix does this a
little bit behind the scenes, if we're choosing JavaScript
virtual machine remix chooses its own fake blockchain that it
runs. If we choose injected web three, as we know, meta mask
pops up, and it connects to our
meta mask, this connection that
remix does, is actually doing something really interesting.
It's not just connecting to our meta mask by some magical
powers. It's actually connecting to our meta mask, which has a
connection to the blockchain behind the scenes. If you open
up your meta mask, and you go down to you select the networks
and you select ad networks, you'll get popped up into a UI
that looks like this. If we go back and hit select networks
over here, we can actually see information abo
ut these
different networks. One of the main things that we can see is
that all these networks have something called an RPC URL. RPC
stands for remote procedure call. And then URL is uniform
resource locator. This RPC URL stands for a connection to a
blockchain node that somebody is running this this URL connects
us to make API calls and to interact with a blockchain node.
Blockchain nodes run with software, and some of them
expose an API call. If you look at the NGO Aetherium website,
there act
ually are instructions for you to run your own
blockchain node for a real blockchain like Aetherium. Most
of these have flags like dash HTTP dot ADR to expose these RPC
endpoints. So if you ever want to run your own node, your own
real blockchain node, instead of using meta masks or any other
the providers that we're going to go through, you can use go
Aetherium, or whatever blockchain you're working with
software to run your own notes. But it's this RPC URL that
allows us to connect to Rinkeby,
if on Rinkeby ropsten, if we're
on ropsten main net if we're on Main net, etc. And it's going to
be the way that we're going to connect to our ganache
blockchain that we're running inside of this application. So
if you look at the top of your ganache here, there's a section
called the RPC server. And this is the endpoint of our ganache
node right now. So what we can do is we can copy this and go
back to our VS code, and paste it in here to see if we can
connect instead of HTTP. With these capit
als, we're gonna have
a B lowercase instead of the uppercase, because the lowercase
is more correct, and it looks nicer. But now we have the
endpoint. Hypothetically, with just this, we can start making
calls and API calls to this endpoint, go to this JSON RPC
specification. Again, link will be in the GitHub repo, we can
actually see different calls we can make directly to our node to
get different information. eath get blocked by hash eath get
blocked by number. And all of these wonderful piece
s in here.
Making these API calls directly is is a little bit annoying to
do ourselves. If you want to do it yourself, you absolutely can
using an API endpoint like Axios or fetch. However, we're going
to use a rapper to interact with our node and do things like
deploy and interact and other such things with our blockchain
node. This is finally where ethers comes into play. Ethers js is one of
the most popular JavaScript based tooling kits, that allows
us to interact with different blockchains a
nd has all these
rappers that make all these API calls and do all these things
with Aetherium, and polygon and avalanche and any EVM compatible
chain. The other incredibly popular package that does the
same thing is going to be web three.js. And you've probably
heard about this and you'll probably see it a little bit
more throughout this course and throughout your web three
journey. The reason that we're using ethers is that ethers is
the main tool that powers the hard hat environment and I
real
ly enjoy it myself. And remember, if you ever get lost
with any of this, you can always come back to their documentation
to install it. As you can see here, you can just run npm
install ethers, we're just going to do yarn, add ethers. And now you should see in your
package json, we now have ethers added in here. Awesome. Now back
in our code, we're going to import ethers into our
deployed.js script so that we can use all the wonderful tools
that comes with so we'll say const ethers equals requir
e
ethers. For those of you doing the TypeScript edition of this
course, this will be import instead of require now that we
have ethers in here, we can create our provider object in
our main function. The reason we we pull ethers outside of the
main function is because we do want to pull our package into
our script. Before we call main, we want to make sure all of this
is done first. So you'll see this is kind of the the normal
setup of our scripts. At the top, we import any dependencies
or exter
nal packages, we'll have our main function, and then
we'll call our main function at the bottom. const is a keyword
similar to let the const keyword makes it so that ethers can't be
changed. So we can't change this. Our ethers variable,
anytime we use const require is a function for us to import the
ethers package. Now remix does all this behind the scenes. But
the way we're going to do it in our code here is we're going to
say const provider equals new ethers dot providers dot JSON
RPC provider
. And then we're going to pass this string as our
provider. So we're saying hey, we're going to connect to this
URL right here. Awesome. So this is the way that our script is
going to connect to our blockchain, our local
blockchain. Now let's get an actual wallet here. With a
private key and a balance and everything, we can see all of
our wallets and private keys in our ganache here. So the way to
get this set up is we can say const. Wallet equals new ethers
dot wallet. And this wallet function
takes in a couple of
input parameters, like a private key, and a provider, the private
key will go to Kenosha and just choose one of these private
keys. And we're going to paste this right into our code. And
then we're gonna do comma provider and save that. Now
pasting your private key directly into your code is a
huge nono. And we're going to learn how to avoid this in the
future. It's okay right now, since we're just using one of
the ganache private keys, and you have no risk of having any
mon
ey associated with this account. These two lines alone
give us everything that we need to interact with smart contract,
they give us our connection to the blockchain, and they give us
a wallet with a private key so we can sign different
transactions. If you remember back to our blockchain basics
section. This is the private key that we're using to sign all of
our transactions to encrypt our transactions. Now that we have a
provider and a wallet, let's go ahead and grab our contract
objects from
these two files here. In order to deploy our
contract, we're gonna need the ABI and we're going to need the
binary compiled code of the contract. So we're going to need
to read from these two files to read from these two files, we're
going to need to use a package called Fs. So back at the top,
we're gonna do const Fs equals require Fs extra I autosave, all
the time, you'll see like, I'll do something and then my white
dot will go away a lot. It's because my fingers have a habit
of pretty much a
nytime I stop typing I save so so please
remember to save early and often, this Fs extra should come
directly with your node project. But if not, you can always add
it with yarn, add Fs extra, and you should see it in our package
dot JSON. Now we can get the ABI and the binary, we can say
const, abi equals Fs dot read file sync, which means we're
going to synchronously read from this file, we could do it
asynchronously. But we want to wait for this file to get done.
So we're gonna say read file
sync. And depending on your VS
code, if you scroll over, you might even get some information
about this function popup like this, which can be really
helpful, we can see that we need the path for the file we want to
read. And then any options as well path the file that we want
to read the at the ABI located at dot slash simple storage,
underscore soul underscore simple storage dot AVI and then
we're going to do a comma of U T, F eight is UTF. Eight is the
encoding that we do for this file here.
Don't worry too much
about what that means for now. So we need the ABI. And we also
need the binary, which is in this second file. Simple Storage
underscore soul underscore simple storage dot bin. So we're
gonna say const binary equals Fs dot read file sync. We're gonna
give it the path Hear, which is going to be dot slash, simple
storage soul, simple storage dot bin, and then the encoding
option, which again is going to be, and it should look like
this. Now that we have the binary, we have the
ABI, we can
create something called a contract factory, which is not
to be confused with the factory pattern in ethers. A contract
factory is just an object that you can use to deploy contracts.
So we're gonna say const, contract factory equals new
ethers dot contract, factory. And we're going to pass it, the
API binary. And the wallet, we pass the ABI so that our code
knows how to interact with the contract the binary, obviously,
because this is the main compiled code in our wallet so
that we h
ave a private key we can use to sign deploying this
contract, then I usually like to write a little console dot log,
saying something like deploying these wait. And we can actually
deploy this contract with ethers by doing const. Contract equals
await, contract factory dot deploy. Now, this is the first
time we've seen this await keyword and you can only use the
await keyword inside of an async function. The reason we want
this await keyword we're telling our code to stop here, wait for
contract
to deploy. And this await keyword means that this
will resolve the promise contract in this contract
factory dot deploy with the await here returns a contract
object. So now I could do something like console dot log
contract. Let's see what happens when we run this code node
deploy dot j s, we scroll up, we see deploying, please wait. And
then we see this massive object that gets printed out. This is
the contract object that we just deployed. And in fact, if we go
over to our ganache, we can se
e that the address that we're used
for our wallet has a little bit less balance and has a
transaction count of one. If we were working with truffle, we'd
be able to see the contracts here we're working with hard
hat, so you won't be able to see the contracts in here. But if
you go to transactions, we can indeed see the transaction that
we just created. This is similar to ether scan. But for our local
blockchain, we can see the sender address, the creation
address, the value, gas price, all this
stuff associated with
this contract. You can also see the different blocks. Since
we've only made one transaction only one block has been mined.
And this is awesome. We have all this other stuff associated with
it. Great job. You just deployed a contract to your own local
blockchain with ethers. Jas. This is fantastic, Awesome work.
Now let me show you what happens if we don't use the await
keyword here. We're not telling our code to stop. We're saying
hey, deploy this contract and then just kee
p going. So we
never actually checked to see if this deploy function finished.
So let's see what happens when we run this instead. Instead of
that big contract object, we get this promise in its pending
state because our code actually finished before our contract
could finish deploying. So we see promise pending here
instead, this is why the await keyword is so important. We're
saying hey, wait for this to finish the await keyword also
resolves a promise. So it'll wait for the promise to exit
it
's pending state. And then it'll return whatever the
pending promise returns. So contract factory to deploy
returns a promise that returns a contract. In fact, if we go to
the ethers documentation, we look up deploy. And here we can
see contract factory methods contract factory dot deploy. If
we look at the definition of the function, it says contract
factory dot deploy takes a whole bunch of arguments and some
overrides this arrow is saying this is what it returns, it
returns a promise that res
olves to a contract. And that's why we
need this await keyword because contract factory to deploy by
itself just returns a promise. But if we do await contract
factory dot deploy, we're saying it returns a promise that
resolves to a contract. And we're waiting for it to finish
deploying to resolve to a contract object. So that's gonna
be a major difference here. Awesome work. So that's gonna be
why this await keyword is so important. And again, you can
only do that in asynchronous functions. So
you need this
async keyword at the top of your function names. Awesome work. So
we've deployed a contract to our ganache. Shane, this is
fantastic. Let's play with this a little bit more. So what else
can we do? Remember how in remix and in Metamask, we could add a
whole bunch of stuff. We could add a gas limit, we could add
some value when we were doing our trends. that we could press
the transact button. We know that when we're working with
Metamask, and we want to send some money between our
accounts, we can actually choose our gas
price, our priority fee, all this other stuff, we can
actually do all that in ethers as well. So if we wanted to
await contract factory to deploy, but with a certain gas
price, we can add these overrides in this deploy
function here. Another really neat trick that your Visual
Studio code might have is if you click Command or Control,
depending on your setup, you can actually click into a function
and see where it's defined and see everything about this
fu
nction. So if I command clicked, I could see that I'm
now in Node modules ethers project contract source of Ts,
all this stuff. And I can see exactly the function definition
of this deploy function on the contract factory object, which
shows us the same code as what we saw in the documentation. We
see we have a deploy function, it takes some arguments, and
then it returns this little semicolon means it returns a
promise that resolves to a contract, which is really nice.
These are in here are act
ually a list of overrides that we can
specify with some brackets. So what we can do is we can put
some brackets in our deploy function here and specify
certain things like for example, we can specify the gas price to
being some number. And now if we were to run this, we would
deploy this contract with a gas price of this, we could add a
gas limit, we can add a whole bunch of different overrides in
our deploy function here. Well, what else can we do? Well, we
can wait for a certain number of bloc
ks for our contract finish
with so we've deployed the contract. But maybe we want to
wait one block to make sure it actually gets attached to the
chain. So we can say const. Deployment received equals
await, contract dot deploy transaction dot Wait, and
specify the number of confirmations that we want to
actually wait. So we'll wait one block confirmation to make sure
that this happened. And then we can do console dot log, this
deployment receipt option. So if we run the code now, we can see
all
this information about our transaction, and we can see
exactly what our transaction looks like, we can see there's
two is no because we're creating a contract from is going to be
this ganache address that we got the private key for, we see the
contract address that we created transaction index, we can see
gas use gas use log bloom block hash transaction, and we can see
all this information about our transaction, something I want
you to take note of is the deployment receipt and the
deployment t
ransaction, I want you to separate these two
because it's going to make your life a lot easier. So we're
going to do quick console dot log, here is the deployment
transaction. And then we'll do console dot log contract, dot
deploy transaction. And then I'm going to copy this whole line by
just typing Command C or Ctrl. C right there. And then here is
the deployment. Here is the transaction receipt. And then
this deployment is receipt is the transaction receipt. So I'm
just going to rename this t
o transaction receipt. To make it
a little bit clearer, which ones which, and we'll run this one
more time. You only get a transaction receipt, when you
wait for a block confirmation. Otherwise, you're gonna get the
contract object, which has the deploy transaction with it, this
distinction will be more important later on. But I wanted
you to know what the difference between the two transaction
receipt is what you get when you wait for a block confirmation,
the deployment transaction or the tran
saction response,
transaction response is what you get just when you create your
transaction. So transaction receipts transactional response,
they're different receipt is what you get when you wait for a
transaction to finish. And then response is just what you
initially get. Make sense? Okay, great. We're gonna go ahead and
delete those. But those lines will be in the code associated
with the GitHub. Now you saw when we actually printed out
those receipts, we got all this stuff in here, because
deploying
a contract is actually just sending a transaction, as we've
said before, so if we want to see what's really going on under
the hood, we can actually create a transaction ourselves and
create a contract ourselves just by specifying the transaction
information. So how will we do that? Let's deploy this contract
again, but only purely using transaction data. So we'll do a
console dot log. Let's deploy with only
transaction data. And this is going to be the way you can
actually deploy or
send transactions purely with
transaction data you can send any transaction this gives you
unlimited flux. ability with the transactions you want to send,
what we can do is we can say const, or let TX which is going
to stand for our transaction equals, and we can just add all
of our transaction information in here. So the first thing that
we're going to need is our nuts, or the number that we only use
once we go back to our transaction count, we're on four
transactions here. So we'll use the non
ce five, because that's
going to be a nonce that we haven't used before. Every time
you send a transaction, it comes with one of those nonces. Right,
so the nonce is a bit of a over overused term, we saw it back in
our blockchain basics that we use the nonce to solve that hard
problem. nonces are also used in wallets and in signers, to send
transactions and they use a different nones for every
transaction. So nonce, when we're talking about wallets
talks about a number associated with a unique t
ransaction.
nonce, when we're talking about blockchain mining is a value
used to solve that hard problem, they both mean the same thing.
They both mean a number only used once. But they're different
in these different contexts. So we're going to use this number
only use once this unique number for a transaction to send this,
so we're going to say nonce is going to be five, we're going to
pick a gas price of, of this right here, we're just going to
use the gas price of ganache like that, we're go
ing to pick a
gas limit of some big number, we'll use 1123456, we'll just
use that we're gonna say two is going to be no, right, exactly
like what we saw in our receipts and responses down here. Since
we're creating a contract value is going to be zero. Since we're
creating a contract, we don't want to send an ether polygon or
avalanche. And then data is going to be that massive binary
object in our binary bit. So in the binary section, we're gonna
copy this massive binary piece, and we're going
to put some
quotes in here, we're gonna do 0x, and paste that in here. So
this massive, massive data piece is the binary that we're
sending. Whenever you send a transaction, you have this this
data object that you can fill this stuff, we're filling our
data object with the binary with the code that tells the theory
that tells our blockchain to deploy our smart contract that's
going to look exactly like this. And then finally, we want to add
the chain ID. As we've seen before with
Metamask, if w
e go back over to our networks, each one of these
EVM chains has a different chain ID Etherium. Main net is one
ropsten is three Rinke, B is four COVID is 42, etc. And other
EVM chains like avalanche like polygon, are going to have their
unique chain IDs as well, or ganache, we can see the network
ID appear is 1337 in some people, so we can just paste
that in here. Some people have run into some issues where the
chain ID and the network ID are different. And the chain ID is
actually 31337. So if
you have a problem with this, try 31337
instead, but it should be 1337. Now this is a transaction with
all this information propagated, which is awesome. However, this
transaction isn't signed. So nobody's sending this
transaction right now this is just the transaction details of
what somebody wants to do, we actually need to sign this
transaction, and then send it to our blockchain. Cons sign, TX
response equals await, wallet dot sign transaction, and we can
pass that TX object. And then we'll
do a console dot log of
the sign TX response in JavaScript. Same as solidity. If
you type two backslashes, before some code, it won't run that
code. So I'm gonna go ahead and comment out the three lines
above. And the way that I'm doing it is by highlighting the
sections and hitting Command backslash, or you might hit Ctrl
backslash, but this is a keyboard shortcut you can use to
quickly comment out entire sections. And the reason I'm
doing this is because I want to show you what happens when w
e
just run scientex response wallet that signed transaction
back in nosh, we see that we have four blocks in here. Let me
ask if we do this sign transaction and we get the
signed transaction response, will we propagate another block?
Well, let's find out run no deploy.js, we get this massive
thing here. But if we go back to ganache refresh, we actually
don't see another transaction sent. That's because we're only
signing a transaction here, we're not actually sending it.
So the signed transactio
n response. This big number here
represents a signed transaction but not a cent transaction,
which is different. You can actually send one of these
transactions by changing this line a little bit. So instead of
scientex response, we'll change this to send TX response equals
await wallet dot send transaction TX then once We send
the transaction here, we can do a wait, send transaction
response, that Wait, one, we're gonna wait one block
confirmation to make sure this transaction actually goes
thr
ough. And we can run node.js. And it looks like it's done. But
if we actually scroll up, we actually got an error here. So
there's this huge, massive thing here. And if we scroll up, we'll
eventually see TX reject error, the TX doesn't have the correct
nuts, which just for some practice, let's go ahead, type
this into Google and see what we get. We actually get a web three
Jas from four years ago, trying to call leaf picked, blah, blah,
blah, blah, blah, blah, blah, blah. It looks like this is a
meta mask issue that they ran into. And they said, you have to
reset your account in meta mask, which we could do. But let's
make this a little bit more specific. Let's say ethers. JS
looks like we don't get it looks like as of right now, from this
recording, we don't get a Stack Exchange eath or a Stack
Overflow question for this trolley, because this is pretty
straightforward. We don't have the correct nonce for our
transaction. But this would be a good time to actually make this
a question o
n Stack Overflow or Stack Exchange Aetherium so that
it shows up first. So we actually don't get the correct
nuts, we're going to want to make this a nonce of four you
can see here account has a nonce of four TX has a nonce of five.
So we actually would want this to be four. Now an easier way to
always get the correct nonce here is going to be actually
just calling the transaction count from the wallet. So back
in the ethers documentation, there's actually some good
samples here on how to assign
ing a message and then how to
actually send these messages. So we can do a weight wallet dot
get transaction count to get the nuts. So back in our code, we
could do const nuns equals await, oops, copy pasted, await
wallet dot get transaction count. And then we can just
place the nonce right here. Now, let's try running this
again. And it looks like this one did indeed go through. We
can verify on ganache here. We do indeed see we're currently on
block five now and we have one extra transaction.
Now we could
go ahead and just run this again. And we'll never have to
worry about actually updating this nonce ourselves. Since
we're just calling wallet dot get transaction count to keep
updating it current block is six and the our additional
transaction has indeed gone through awesome. I showed you
how to actually sign the transaction. But we didn't sign
the transaction for our central injection. Well, why not? If you
command click or control click, or you go to the documentation
for ethers,
we can see the code for send transaction. So first,
it does some check provider stuff. There's some stuff to
populate the transaction. But we can actually see that before it
sends the transaction even in ethers, it signs the transaction
first, and then calls this dot provider dot send transaction.
So if you just call send transaction with the transaction
details like we did here, it's the same as signing it first and
then sending it with the provider. Okay, great. So we've
learned how to send a
transaction using pure
JavaScript and using pure ethers. One of the main
takeaways from this is that every time we change the
blockchain, every time we change state, every time we use gas,
we're sending a transaction that looks pretty much exactly like
this, the data is going to be the differentiator, the data for
us here was data saying to create a new contract, when we
make transactions, like adding people or storing the data that
we're going to be passing in our transaction is going to be dat
a
associated with calling these functions. And when we actually
call functions in ethers, or in Hardhead. We're not going to do
this kind of raw const, TX, and list out all the stuff like here
and list out the raw data, right? Because that's really,
really hard ethers and hard hat are going to make this process a
lot easier. So for now, let's go ahead, comment out this whole
section, which again, if we copy this whole thing, and then hit
Command, slash, or control slash, or whatever the shortcut
is on your environment, that'll actually comment this out. Let's
go ahead and uncomment this section so that we deploy our
contract using kind of the ether is much easier to read way than
this weird TX stuff. Oh, cool. So we've changed our script back
to deploy our contracts like this. Now that we've actually
deployed our contract, we can learn how to interact with our
contract through code as well, the same way that we click these
buttons in remix, we're going to code it out for ourselves here
.
So if we look at symbol storage, we have a button for add person
for store. And then we have these view functions as well.
The easiest one is going to be the Retrieve function, which
grabs our favorite number, retrieve returns favorite
number. So we can call that in ethers by doing const. current
favorite number equals await. contract.we retrieve. The
contract object that we have is what's returned from our
contract factory as long as we've awaited it the contract
object is going to come with
all the functionality described in
our API. That's why we had to pass the API to our contract
factory. If we look inside our API piece here, we can see it
has a ton of information on the different functions that we can
call and the types that it has and the return types and
everything like that. For example, if I look up retrieve,
I can see down here, we have retrieve, we can see that the
name of this function is going to be retrieved. And the outputs
are going to be a un 256, of type Nuun 256.
Now, this is a
little bit difficult to read because it's not formatted.
Since we call this dot API, it's going to be a little bit hard to
read. But we could change it the dot JSON. And then you'll see
it's highlighted a little bit, I can even do format document with
prettier. And you'll see it actually formats to be a lot
easier to read now and go back to retrieve. And we can see,
this block of code here defines what the Retrieve function can
and can't do. I'm going to change it back to abi. And
it
looks like my formatting has stayed, which is great. This is
much easier to read than it was before. As I said, the ABI or
the application binary interface is incredibly important for
working with our contracts. If we give our code just this huge
bytecode thing, it's gonna be really hard for any processor to
decompile this or understand what exactly what the functions
are that are going on here. There are decompiler options out
there, like ether VM, that IO slash decompile. That can
decompil
e some bytecode into the solidity. But it can be really
tricky to get it exactly right. So it's much easier just to have
the ABI to say, hey, this lump of code, this lump of numbers
and garbled nonsense is this. When we deploy this bytecode to
the blockchain, and we call functions on it, the code will
automatically allow those functions to get called if they
do exist. But in order for our code to know that they exist,
it's much easier just to give it the API. So we can get our
current favorite n
umber like this. Let's go ahead and console
dot log, the current favorite number. Now that we've edited
this code, we're going to let me zoom out a little bit. Now that
we've edited this code, we're going to connect organ nosh
instance, we're going to connect a wallet with a private key that
we got from the top of our ganache here, we're gonna grab
the ABI and the binary of our contracts, and connect them to a
new contract factory object, which is connected to that
wallet. So that wallet will be
the one to actually deploy the
contract, we'll deploy the contract with const contract
equals await contract factory dot deploy, we will wait one
block for that transaction to finish. And in fact, we're not
going to use transaction receipt. So for now, we're just
going to delete that part. We're not going to do any of this
here. So I'm going to delete it for now. However, I'll leave
this section commented out in the GitHub repo. And then we're
going to call contract retrieve which should return
our current
favorite number. Since this is a view function, this contract
call won't cost us any gas. If we look at simple storage, that
soul we can see retrieve is a view function. And remember view
and pure functions, if called outside of a contract function
call don't cost any gas, we're just reading up the blockchain,
we're not changing any variables on chain, we're not changing the
state of the blockchain. So this won't cost any gas. So let's go ahead and run this. Perfect, we
get deployin
g, please wait. And then we get this big number
response. So what's this big number response here? Big number
is a library that comes with the ethers application that helps us
work with numbers. If you actually scroll down, they even
have a section saying why can I just use numbers, you'd expect
current favorite number to just be zero, but it returns this
weird hex thing that says his big number true this weird, big
number thing. So solidity can't use decimal places, and
JavaScript has a hard ti
me with decimal places. And this is kind
of the more specific rationale for why not to use numbers, what
you'll see a lot of the time instead of numbers is you'll see
strings like zero, you'll see JavaScript use strings like
this, or big numbers. Now, if I were to try to pass a number
like this in JavaScript, this number would be too big for
JavaScript to understand. So we want to use big numbers or
strings when working with ethers. Now we can make this
more readable by adding.to string at the e
nd and printing
out the string version of this big number. Now, if I rerun this
code, we can see we get zero, which makes sense again, because
our favorite number gets initialized to the zero value,
if not specified, and we haven't called store yet. So awesome. So
that is, that's working perfectly. So this is what our
current favorite number is. Let's make this console dot log
a little bit more syntactical. We're going to use something
called string interpolation. So we can interpolate our strin
g
here with variables. Typically in JavaScript when working with
strings, use double quotes. However, if you want to mix
variables with actual strings, you can use backticks instead.
So we're going to use some that backticks here, and we're gonna
say current favorite number to call in here. And to tell
JavaScript that this is a variable that we want to read,
we put a little dollar sign and a bracket around it, like this.
Now, if we run this code again, it's saying deploying, please
wait, and we
get current favorite number is zero, because
JavaScript goes, Okay, this is a string, ah, dollar sign, curly
brace looks like this is going to be some variable or some
JavaScript that you want me to interpret and close it off, and
then backtick. Cool. So our current favorite number is going
to be zero. Great. So let's update on the contract the
number by calling the store function. So we'll say const,
transaction response equals await, contract dot store, and
we'll add seven. Now since seven is
a small number, you can just
pass it like seven. But passing it like seven in a string also
works. Again, this is because if we want to pass some crazy
massive number, JavaScript would get confused. So it's usually
best practice to pass variables to contract functions as
strings, even though that might be a little bit confusing ethers
is smart enough to know that this seventh string is actually
seven the number then we can do const. Transaction received
equals await transaction response that Wai
t, one. So
we'll wait one block here. This is similar to us doing contract
dot deploy transaction dot Wait, the syntax here is a little bit
different than what we saw up here. Because this is using a
contract factory. And this is calling a function on a
contract. So when we call the function on the contract, we get
a transaction response. When we wait for the transaction
response to finish, we get the transaction receipt. Now I can
do const, updated, favorite number two equals await on track
dot
retrieve. And then console dot log updated. favorite number
is, and we'll do a little string interpolation, updated, favorite
number like that? Now let's go ahead and run this. So in this
process, what are we doing, we're deploying the contract,
we're getting the initial value, we're going to update our
contract by calling store which is going to cost gas. So this is
a transaction, we're going to get the transaction response,
then we're gonna get the transaction receipt, we're not
going to do a
nything with the transaction receipt, I want to
ingrain in you all that these two are different transaction
response and transaction receipt, and you'll see why in
the future. And then we'll get the updated favorite number, and
then we're just going to print it out. So let's do this. Boom,
and perfect. Deploying, please wait, current favorite number is
zero, updated favorite number is seven. And if we go to our
ganache instance, we go to transactions, we can see, we now
have a contract call at t
he top, we have the sender, the contract
address, the gas price, all this stuff, and we see our
transaction data right here. So this transaction data is what
gets sent in that data slot of our transaction object. Ethers
is just doing that on the back end for us so that we don't have
to make that big transaction object there. All right,
awesome. You've successfully deployed a contract to your own
local ganache instance, or your own JavaScript virtual machine.
This is great. Now let's clean this u
p a little
bit. Because if we look up here, we have both our connection to
the blockchain and our private key stored directly in our code,
we were to push this code up to a GitHub or some other code
repository, if you will, will be able to see our code. If we look
into ploy dot j s of my code, ah, there's something else in
here, we don't actually see the private key or the RPC URL in
here. So what's going on? Remember, if you give out your
private key, whoever has your private key owns your fund
s. So
even though this is a fake, private key that doesn't have
any real money in it, we still don't want to have to hard code
our private keys into our code, just in case we accidentally
share code with somebody. So what can we do? Well, one of the
most popular methods is actually creating something called a dot
EMV file or an environment variable. And if you're familiar
with environment variables, you can actually set them right in
your terminal, but we're gonna set them in our dot env. So wha
t
you want to do is you're gonna want to create a dot env file.
And this is going to be a file where you store its sensitive
information. And this is going to be a file, we're never going
to share this with anybody. This dot env file will stick
variables of our choosing into the environment of our code. So
for example, if I pull up my terminal here, and I do echo
cat, this is going to reflect what the cat environment
variable is for us. Right now there is no cat environment
variable. However, if
I do export, cat equals dog, and now
I do echo dollar sign cat, I get dog output. This is what an
environment variable is, it's a variable in our terminal or in
our scripting environment, since I don't want to have to type
export, private key equals blah, blah, blah, every time, what
we're going to do instead is we're going to stick them into
this dot env file. So when this dot env, we can put private key
equals. And we can grab this private key from our script, and
paste it in like this. And a
note, some tools look for the
0x, at the beginning of the private key ethers, and hard hat
is smart enough that either one works, but if you run into some
issues, just know that sometimes you might have to put your 0x at
the front of this. Great. So now we have a private key in an
environment variable. What do we do now? Well, in our deploy.js,
we want to grab this environment variable and stick it into our
script here, so that our script can then stick it into our
environment. So we're going t
o add a tool called dot env. To
make this easier, so we're gonna do yarn, add dot env. And if we
look at the dot env package, can read more about it and read
about how to add it with NPM. Again, we're just using yarn
add. But then we can just call this require dot E and V dot
config. And this will pull in all our environment variables.
So we can just do require dot EMV config, we should see
this on our package that JSON, we do. Excellent. Now that we
pulled it in, we actually get access to our p
rivate key
environment variable, you can access environment variables in
JavaScript, are using process dot EMV. So instead of putting
our private key here, we're gonna delete that whole thing
and substitute it with process dot EMV, that private key, and
we'll save and it'll reformat for us to make sure this is
actually working, we can just hit up after we save up, up up
up up a bunch and go back to no deploy to Jas. And we see we get
the exact same setup. And if we scroll back, you can see a
tra
nsaction has indeed gone through. If you want to double
check that this is actually printing out your private key,
we could do console dot log, press study and via private key
we run, we see that that is indeed the private key coming
from our environment variable here. Awesome. Now our RPC URL
here is isn't really something that we need to secure however,
maybe we're going to use a certain API key or maybe a
certain endpoint that only we want to have access to. And we
don't want anybody else to
be using our RPC endpoint. So we're
going to add this to our dot env file as well. So in our dot env.
When I copy this here, I'm gonna say RPC URL equals, and I paste
that in there, just like that. And we're gonna delete here. And
we're gonna do the exact same thing, say process dot e NV dot
RPC URL. And now we're going to run this again. And I'm going to
take out that console dot log. And we should get the exact same
response because all we did was swap out our RPC URL with our
environment vari
able, which is going to be exactly what it was
before. So we go ahead and run this, and we get the exact same
response. And if we go to ganache, we see that we do
indeed get a transaction here, which is perfect. Awesome. So
we've learned how to add environment variables to our dot
env file. So that so that just in case, we want to share our
code, or we push our code up to GitHub, which we'll do in later
sections, we don't accidentally expose our private keys or our
RPC URLs. Now all the code tha
t we have in our project here, if
we push it up to GitHub, or share it with somebody else, all
this code will get pushed up, including our dot env file.
However, if you look at my code samples here for this course, we
don't see a dot env file in here. So how is that possible?
Well, what we want to do whenever we have a project is
create a dot Git ignore file. And in here, we want to put in
v. And we also want to put in Node modules. This means that
when working with Git and working with version
control,
which we're going to do a little bit later, we won't push our dot
env file up to GitHub. And we also won't push up known
modules. So if we go back to my example here, we don't see, we
don't see a dot env file, but we do see a dot env dot example,
just to show you what one would look like. So it doesn't really
matter that I have them in here. Now, if you're really paranoid,
there's something else you can actually do when running your
scripts and running your commands. Let's say you didn'
t
want to put your private key into a dot env file because you
were nervous that you would accidentally push it up or
something. What you can do is you can add your private key in
your RPC URL as environment variables right in the command
line. So before you run Node deploy, that's as well you can
do, you can say RPC URL equals, paste your RPC around. And then
you can say private key. equals and then paste your private key
and then do node deploy.js. Setting these right before we
run our script
is the exact same as if we had set them into dot
env. Here If we hit run, we see the exact same output, which
means that our RPC URL and our private key went through
successfully. This way for key management is fine. But our
rights doing this, it's going to be much better. But it still
makes me a little bit nervous in our development environments
with our fake private keys, having our code in the dot env
file like this is, is okay, right? Because we don't really
care if this key gets hacked, lik
e nobody's using it. But when
we move to a more professional setup, this can be a little bit
scary. So how can we make this even more secure? Well, what we
can do is actually we can encrypt our private key and
store our encrypted key locally. That way, if for some reason
somebody does get into our account, our private key isn't
just sitting around in plain text, it's encrypted. And you'll
need to know a password that only you know to get into it. So
how do we add that? Well, first, we're going t
o create a new file
called encrypt key.js. And this is some code that we're going to
use to actually encrypt a key. And we'll store that locally
instead of our private key in plain text. This will make us
even more secure so that we don't have our private key just
hanging around in plain text here. So let's go ahead and
build the script to encrypt our private key. So we're going to
use the exact same setup as we did for our deploy script. We're
going to do an async function main. And then down h
ere, I'm just
gonna go ahead and copy from deployed J S. We're gonna use
this exact same setup, and paste it. Okay, great. We're gonna be
using ethers J, s, and r dot E and V again. So we're going to
add these in const ethers equals require ethers const, FS equals
require Fs extra. And then require dot env. Config.
Alright, so right now, in our dot env, we do have this private
key. And again, if you don't want to have the private key in
their way you can just do is you can do private key equals
and
then you know, node, whatever script you want to run. So we're
going to set this script up to run our encrypt key one time.
And then we can remove our private key from anywhere in our
workspace so that it's no longer in plain text anywhere. So what
we want to do is we want to say const wallet, and we're gonna
create a new wallet, but a little bit differently. We're
gonna say equals new ethers dot wallet, process dot E and V dot
private key. So we do need our private key to stick in here.
But
then once we create this wallet, we're gonna say const.
Encrypted JSON key equals await ethers dot encrypt, this encrypt
function is going to return an encrypted JSON key that we can
store locally and that we can only decrypt it with the
password. And it takes two parameters. It takes a private
key password and a private key. So in our dot EMV, just for
right, now we're going to create a private key password. And I'm
going to say it's password. But obviously, this is a terrible
password. And yo
u should never use password as your password.
But for now, we're just going to leave it as password since I'm
encrypting this big key anyways. So we're going to encrypt it by
passing the password process study and v dot private key
password. And we're also going to pass the private key, it's
going to be ethers, it's going to be wallet dot encrypt. We're
also going to pass it process dot EMV dot private key. Now
let's go ahead and run this right now. And then we'll
console dot log out this encryp
ted JSON key and see what
happens when we run this. So to run this, we're going to do
node, encrypt key.js. And hit enter. And we'll see what
happens when we console log it out this JSON object here is
what our key looks like, encrypted. So it's got the
address this ID version, all this other stuff. And all this
other stuff is the encrypted version of this key. If somebody
gets into our account and they see this, they'll have to know
the password to decrypt this private key, they'll need to
know
the password to decrypt this JSON object back into a
private key. So what we're going to do, now that we've encrypted
it, we're going to save it. So we'll do Fs dot write, file
sync, we're going to pass it to dot slash dot encrypted key dot
JSON, comma encrypted JSON key. So we're saving it to a new file
called dot encrypted key dot JSON. And we're passing it this
encrypted key that we just made. So if we open up our file
explorer, and we run this command, you'll see we get a new file
called do
t encrypted key dot JSON. And it's this encrypted
key here, which is awesome. So now what we want to do in our
dot Git ignore is add dot encrypted key dot JSON, so that
we don't accidentally push this up to GitHub. And now we have an
encrypted key and we can go to our private key and delete this
from our dot env file. We can also delete our private key
password from our WMV file so that the password isn't just
hanging around in plain text. Now that We have an encrypted
key. Back in our deploy sc
ript, we can change the way that we
actually get a wallet. So at the top, we're getting our wallet
just by passing in the private key like this, we're not going
to do that, we're going to use our encrypted key that we just
created. So we're going to do is we're going to say const
encrypted JSON equals Fs dot read file sync. That slash dot
encrypted key dot JSON, comma UTF eight, this Fs dot read file
sync is just going to read from our encrypted key dot JSON into
this encrypted JSON variable her
e. Next, we're going to
create a wallet from this encrypted key. We're gonna say
let wallet equals new ethers dot wallet, dot from encrypted JSON
sync. And all these commands that we're working with ethers,
we can of course, find them in the documentation from encrypted
JSON sync takes the encrypted JSON and a password and returns
a Wallet Object. So we're going to pass it that encrypted JSON
that we just read. And then we're going to pass it password
which we're going to do process dot EMP dot
private key
password. And then finally, the reason I use let here is because
now we have to connect this wallet back to our provider. If
you look here, we're not connecting our wallet with a
provider. When we make our transactions with our contract
factory, we need to make sure the wallet knows about the
provider here. So we can just say wallet equals await wallet
dot connect provider. Now, if we run our deploy dot j s with our
private key password as an environment variable, it should
still dep
loy. So we can do private key password equals
password, which yes, we know is terrible. But that's what we're
using for now node deploy dot j s, we should get the same output
we've been seeing this whole time and we do we're able to no
longer have our private key in our dot env file not in
plaintext anymore, it's in this encrypted key. So that just in
case somebody hacks our computer, they still won't be
able to send in new transactions unless they know the password.
This is awesome. One more th
ing to know, if you type history, if
somebody got into your computer, a hacker could actually see
private key password equals password in your bash history.
If you run history, dash c, you actually will clear your
history. Now if I type history, I can just see that the most
recent command I wrote was history, this is really just
some of the bare minimum for encryption and keeping your key
safe. And it might seem ridiculous that somebody might
be able to hack your computer and read your encrypted
private
keys and everything. But as your projects get bigger and bigger,
it is really important to know about private key security and
private key safety and, and for this course really just giving
you the bare minimum here and showing you how to encrypt keys
and how to be a little bit safer here. Now for the rest of this
course, we are going to be just using this syntax with our
private key in a dot env file. The reason why we're doing like
this for the rest of the course is it is a little bit
easier,
I'm really hoping Hardhead add some additional features to make
private key encryption much safer and also easier to use in
the future. And they probably will. And the other reason that
we're okay to do this here is because you've solemnly sworn
that you're not going to use an account that has any real money
in it for the duration of this course, you're only going to use
private keys that have tests on Aetherium or are fake private
keys like this one that we got from ganache. In fact ju
st to
really hone this in in the smart contract kit slash full blocked
in solidity course, Jas GitHub repo in the discussions tab, you
go to announcements, I've created one called the dot end
pledge. Because recently I've seen too many people follow a
tutorial that doesn't tell them about the security risks of
doing this. And I've made this dot EMV pledge, I would love
everyone to jump on and read. And if you agree, at the bottom,
leave a comment saying I will be safe, I will be safe, make sure
you read and you understand what's going on in here. And I'm
not doing this to scare you. Because again, at the end of the
day, if you use a Metamask that only has tests that funds for
the duration of this course, you will never have to be worried
because of your key gets compromised. It's just test it.
So who cares? This is if you're using a meta mask or you're
working with a meta mask that has real funds. So I'm going to
read up the pledge because it is really important. You understand
this wh
en you're working with real funds. And if you're like,
Hey, I'm not working with real funds, I don't care, great move
past this, whatever. But when you do work with real funds,
when you do decide, hey, I actually want to deploy this to
a real network. Now I need real money to do that. Come back to
this pledge. Scroll the bottom say I will be safe and make sure
you read and you understand this. Okay, so the pledge is, I
solemnly swear that I will never place a private key or secret
phrase or pneu
monic in a dot env file that is associated with any
real funds. Basically, you basically never have your
private key or your pneumonic phrase, in plain text, anywhere.
You'll all only place private keys in a web file that only
have tested e FF link or other cryptocurrencies. Because again,
if your private key has only test net funds, then that's
great. I don't care, we are aware that if we forget a dot
Git ignore, and we push our key phrase to GitHub, even for a
split second, or even show our ke
y slash phrase on the
internet, wherever it may be for a split second, it should be
considered compromised. And you should remove all funds
immediately. So even if you deploy your private key to a
website, and then immediately delete your website and think,
Oh, nobody probably got to it, you should consider that private
key compromised, and you should remove all your funds. And
again, this is just for real funds. If your private key was
only tested, funds gets compromised. Well, who cares? I
do
that all the time, you've been seeing me do that all the
time, because it only has tested funds in it. If at the end of
this course, you want to steal all of my tests that funds I
mean, have a blast, it would be annoying to me at worst. If I'm
unsure if my account has real funds in it, I will assume it
has real funds in it. So if you don't know if it has real funds,
assume it has real funds, and you will not use it for
developing purposes. And then finally, I am aware that even if
I hit Add Acco
unt on my meta mask or other eath wallet, I
will get a new private key, but it will share the same secret
phrase slash pneumonic of all the other accounts generated in
the meta mask or other eath Wallet. So if I'm in my meta
mask here, and I hit Create account, I will get a new
private key with the new account. However, all of these
accounts that I've created with this Create Account button have
the exact same pneumonic phrase or secret phrase. If I import an
account with a private key, it's goi
ng to have a different
pneumonic phrase. But all of the ones that I generate inside the
wallet are all going to have the same phrase. Okay, great.
Hopefully that'll make sense. I have some pledge additions here.
For this course, I will only use funds associated with a brand
new never before use meta mask or other eath wallet. Again,
this is not to scare you, if you just work with a brand new meta
mask, you don't have to worry about any of this and just refer
back to this when you start looking a
t real money and real
private keys. I'm aware that my account associated with my
private key is the same on test nets that it is on Main nets. So
like I was showing you, my private key on Rinkeby is going
to be the same as my private key I'm on a main net, if I must use
a private key associated with real funds in the future, until
I am 100% sure what I am doing, I will always either use one of
the encrypted methods that Patrick showed you some better
encryption stuff that I didn't show or use th
e command line way
to pass private keys and then delete the command line history
right after. If I'm never actually deploying anything to
make that myself or work with a private key with real funds. I
do not need to be concerned. Take a look at this. Read this
internalize it, it should make you confident. Now again, I'm
not saying this to scare you. I'm saying this to instill
confidence in you that these are some of the things that we want
to think about. Okay, great. In here, I will be safe. Bo
om, I will be safe. And if you
want to copy paste this on Twitter, put this in a huge
tweet thread. Go for it. The more people who know about this,
the more people who understand the security risks of their WMV
files and their private keys, the better. So thank you for
listening to this. I know I definitely belabor the point,
but it is really important. Let's continue with the course.
Alright, so we're just about done here. However, there's one
or two more things we want to do just to clean this
all up right
now when we're auto saving reason, the VS code plugin for
us to auto format. However, in the future, if anybody else
comes across our repository, they might not have the Vyas
code auto format or on. So we want to give users a way to
format their code. So it matches the styles that we use. So we
have prettier the extension installed. We can also add
prettier as a Node js module that can tell other users who
don't have a Vyas code exactly how to format both their
JavaScript and the s
olidity. There is a prettier plugin
solidity located here, there will be a link to it in our
GitHub repo. And if we scroll down, we can see how to install
with npm install dash dash save Dev, which again, we're just
going to use yarn, so we're going to do yarn, add prettier,
and then prettier plugin solidity. So we're installing
both prettier, and the solidity plugin for prettier. And if we
check our package, JSON, we can see that these two have been
added. And what we can do now is we can creat
e a new file called
dot prettier, R C. And in this file, we can define some little
curly braces. And here, we can define what we want for both our
solidity and for a JavaScript, so for example, our simple
storage has a tab width of 412344 spaces. Maybe we want to
change that. We want tab width to be two. So we would save it
here and come back to simple storage To save it here, and it
would get auto formatted to r dot prettier RC. So in our
settings here, we have the default editor for solidity,
our
hard hat, solidity plugin, and the default for meta for
JavaScript being the prettier vs. Code one, when we add
prettier RC in here, this file will take precedent over the
default configuration, so long as we have downloaded the module
in our node modules, which we can see it right here. And we
have this dot prettier rc file. I'm going to keep the tab with
form. So we're going to update it to that one thing that we
currently do have that I do not like is these semicolons at the
end, so we're
going to do semi boss, and I'm going to save
this, come back to deploy it save and you'll see the
semicolon automatically goes away. I'm also going to add use
tabs false, since I want to use spaces or spacing, and then
single quote, false. This way, we'll always use a double quote,
instead of a single quote, in JavaScript, you can actually use
the single quote or double quote, to define strings, but
we're going to make it so that no matter what quote you use,
it'll always be double quote. And t
hen for all your open
source repos, and for all your projects that you make, you want
to make a readme.md readme files are generally where people put
instructions or information about your project or anything
like that. This way, whenever anybody comes across your
project, they'll know what it's about your readme dot MDs, our
Markdown syntax again, remember how when we made that trial
discussion, we use some interesting tips to format our
solidity in our code here. Well, that formatting process
is the
exact same for.md files for markdown. They're both going to
use markdown. In fact, if we hit Ctrl, Shift V, you'll enter
preview mode for the markdown you'll see pound sign here in my
preview of the hard hat. This ethers simple storage FCC is
huge. And it looks like a heading at the top. So Command
Shift V to view your.md files or or MIP Ctrl, Shift V for Windows
and Linux users. The last thing that we're going
to do is we're going to deploy this to a test net and interact
with this on a
test net, we're going to use the Rinkeby test
net. But be sure to use whatever recommended test net the GitHub
repository for this coerce recommends. Now looking at our
code, you might already have a good idea of how to actually
make this slight change. Based off of our last section, we know
that all we need is an RPC URL and a private key and we can
begin making transactions on a blockchain. So we're probably
going to need a rink B RPC URL, and a rinky private key. Where
can we find both of tho
se, if you want to do everything on
your own, and in full decentralized context, we could
run a rink B version of geth, we could run it locally, and then
just connect to our guest node, we're not going to be showing
how to do this here. However, this is 100%, something that you
could do. Instead, we're gonna use a third party RPC URL in the
GitHub repo associated with this course. Go to Lesson five, we
can scroll down to get a link for alchemy. Alchemy has a node
as a service and allows us to co
nnect to any blockchain that
they have support for two alternates might be quick node,
Morales, or infura. These all have node as a service options.
But we're going to work with Alchemy, because it's the one
that I liked the best, we can go ahead and get started for free
or login or create a new account, I'm going to go ahead
and sign up with our hard hat Free Code Camp user. And we're
going to select the Ethereum blockchain ecosystem. Let's go
ahead and create our first app. This is going to be
we'll call
it Free Code Camp. RT hat. Our app name will be ethers, simple
storage, FCC, and the network is where we're going to choose rink
B. But you can see in here, we can actually choose more than
just rink B, we can choose really Kovan, rink B, ropsten.
And layer twos like arbitrage and optimism, we're going to be
using rink B. So let's go ahead and create this app. We're going
to choose the free plan and hit Continue. We're going to skip
adding payments. If you want to tweet your referral
code, feel
free to tweet your referral code. We're going to skip for
now. And we're going to keep it at capped capacity. Since we
don't have a we don't have a key in here. And then how did you
hear about us go ahead and give me that shout out at Patrick and
Free Code Camp and then hit Let's go. Now we get to the
alchemy dashboard, where we can see a ton of information about
our node and different ways to connect to the nodes and and
stuff like that. This is going to be really similar to to this
RPC server endpoint of ganache, except it's going to be a
connection, except it's going to be a connection to a real test
net or real main net. What we can do now is we can select our
our app that we just made. And we can hit View key. And we can
see here we get an API key Key, we add an HTTP endpoint and we
also get a WebSocket. We're only concerned with the HTTP
endpoint. This is going to be our RPC URL that connects to
rink B. So what we can do now is we can copy this and come over
to our Vi
sual Studio code. And in our dot env, we can
substitute these out for their actual tests and values. So for
RPC URL, we're going to delete this and replace it with our
Rigby RPC row. And now how do we get a private key for an actual
test set that has actual rank be on it? Well, here is where we
can use our meta masks. So back in your browser, go over to your
meta mask, select the three dots, go to Account Details,
export private key. And this is where you can export your
private key, type in you
r password and boom, you now have
your private key for your account on meta mask. Now
remember, please, please, please don't continue with a meta mask
that has actual money in it. A quick way to check is by going
to your networks tab and seeing if on any of the main nets or
the networks with actual money in it, you see any money, I
don't have any money in this. So I know I'm good to go. If you
have tests that money, that's fine, because that's fake money
anyways. And again, most browsers have a
profile
mechanism where you can create a new profile for you to use. But
here now that I've copied my private key, we can come back to
our Visual Studio code, paste the key in here. And now I have
a private key that has actual rink be in it. Awesome. And
remember, if you ever get low, just come over to faucets dot
chain dot link slash Rigby get some tests eath I'm not a robot
will send the request. Now that we have our private key in our
rink being here, we can now try to run this on an actual t
est.
Net, we look at our code, we see we're grabbing an RPC URL, which
is going to be from r dot end, we're grabbing a private key
which is going to be from our daughter Yun V, which points to
our rink D Metamask. And our rink B blockchain. So let's just
add a console dot log under our contract deployment so that we
know what address it's at. So we'll do console dot log, we'll
do some string interpolation, contract, address, contract dot address. All right,
great. Now let's go ahead and run this
. So we'll do node
deploy dot J. S deploying Please wait, you'll notice this takes a
lot longer. Because we're deploying to a test net instead
of our own fake local blockchain, test nets and real
networks often will take a little bit longer because they
need to wait for the blocks to propagate the transaction to go
through, etc. But after a brief delay, we will indeed see that
we get a contract address here. And we have a current favorite
number. And it's being a little slow again, because we're
waiting for our next transaction to go through to update the
number and boom, looks like we've successfully updated it.
Now something that's important to know if ever you run command,
and you want to kill it, you can do Ctrl C, and that will stop
it. So any command in the terminal that you want to just
abort Ctrl. C is your Get Out of Jail Free card and that will
kill it. That'll stop it wherever it is. So we'll use
Ctrl C a lot in the future. So now let's grab this contract
address and go over
to Frank V ether scan and paste it in, we
can see our two transactions here, we can see we have a
contract creation. And we can also see we call a store
function. This is awesome. We've successfully deployed a contract
to the wrinkly chain using our own code. Congratulations, this
is massive. Now on ether scan, we actually can verify and
publish our contract code. What is verifying and publishing your
code? Well, right now, our code looks like a huge gerbil of
bytecode. And anybody looking at o
ur contract directly on chain
will just see this huge jumble of bytecode, we can use a
decompiler to try to decompile the bytecode into what it looks
like in solidity. But this can often take a long time and, and
a lot of processing power. So instead, we can just make it
much easier by verifying and publishing the code ourself, you
go ahead and hit verify and publish. We can scroll down and
we can add compiler information to compile this on ether scan
and other block explorers. This is a single
file compiler
version is zero point 8.7. And it's open source is licensed is
MIT. Let's go ahead and continue. And we're going to
copy paste our solidity code into this large section. Paste.
We don't have any constructor arguments so we can skip this
section. We don't have any libraries or any other
miscellaneous settings. So we'll select I'm not a robot and we'll
hit verify and publish. You might have to wait a few
minutes, but awesome. Our contract was successfully
compiled. Now if we go back
to contract source code, we can see
all the code in here. And if we grab our contract address, place
it into place into the search now, and we go to contract, we
get a little green checkmark. And we can see, anybody can now
read our source code. Additionally, those buttons that
we saw and remix for reading from our contract, and writing
to our contract, are in this read contract and this write
contract. If we read the contract, and we retrieve the
most recent number, we do indeed see that we hav
e seven here
because we recently stored seven. Awesome, quick note, this
might already be verified for you, since ether scan might get
smart enough to notice that a lot of people are deploying the
same bytecode it's already verified for you just go ahead
and walk through these steps anyways. Now the code
verification we just did was pretty simple and
straightforward, because our code was pretty simple and
straightforward. Using larger and more complex code can make
the verification process a lit
tle bit harder. Additionally,
we don't always want to have to click buttons on ether scan. To
verify our code, we want to do it programmatically. So in later
sections, we'll learn how to verify all of our code directly
through our code editor, you can imagine the process is this easy
for deploying to any EVM chain in our alchemy, we could easily
create a new app and change our network. And you could see how
easy it would be to just switch out this RPC URL and your
private key to work on a differ
ent chain. This process is
also the same for harmony Phantom, avalanche, etc. And if
we wanted to switch chains, we would just switch the RPC URL
and switch the private key, and everything else would stay
exactly the same. Now, Alchemy also shows us and can teach us a
lot about transactions and about things that are going on behind
the scenes, including a concept called the mempool. To help us
understand a little bit more about those transactions that we
just sent, and how to work with Alchemy.
To see more about our
transactions, we have Albert from the alchemy team to give us
a little demonstration. Hello, Albert here from alchemy,
I'm that guy in tech on Twitter, feel free to follow if you want
to engage and ask any questions about this section of the video.
But super excited to join Patrick here to explain a little
bit of what goes on behind the scenes when you are using
alchemy to submit a transaction. And we have a ton of tools to
actually provide a window of visibility into what'
s going on
so that you can actually debug in case there are usage errors
on your website, or there are pending transactions that are
stuck. Whatever it is, we provide that window into the
data that you control. Remember that all the transactions that
you submit are recorded on the blockchain. They're not
controlled by alchemy, they're not controlled by any other
service provider. We are just a window, we're just the plumbing
the piping to be useful to you. So let me show you exactly what
that me
ans. Right now I have a bunch of applications in my
dashboard. You can see here that there are different projects
that I've used over time. This one is the most recently active,
and it is the one that I have currently set up to connect to
my meta mask. So actually use a custom RPC provider here. And
let me make my face a little smaller. And you can see here,
I've misspelled Rinkeby. But this right now, my my network is
actually connected to the Rinkeby test network via
alchemy. So this is actual
ly this application. So if I click
into here in the dashboard, you can see here, a bunch of really
interesting statistics, this is the first thing that you'll
probably use. If you're trying to understand more about your
application, you'll go here and you can see how many compute
units per second your application is currently using.
And this is kind of great for specifically alchemy usage,
understanding. But then this is also really useful to see like,
what's the median response time, and so 33
milliseconds is pretty
good. If that starts to increase, then you might want to
figure out, you know, what's going on here, success rate, it
has been kind of low. So that is a clue for me to click on this
tab to view recent invalid requests. And that I can
actually see oh, there's a bunch of failed transactions where the
transaction has already been sent or the nonce is too low, or
whatever it is, I can actually use this tab to debug. So that
success rate is pretty useful. Throughput that's been
limited.
So if you are sending too many requests or your website is
getting spammed, you might start getting some requests blocked.
So that's what's useful to view their concurrent requests over
here, success rate in the past 24 hours versus the past one
hour, the total number of requests in the last 24 hours.
And this is different than compute units, because each
request can have a different level of computing cost. And
computing cost as measured by compute units. Total requests is
just the ac
tual number of absolute requests. And then of
course, the number of invalid request. Cool. So one thing I do
want to show you that's interesting is when I do submit
a transaction, and I actually have one right here, I want to
send so let's transfer between my accounts, and I'm just gonna
send it to tiny amount of Rinkeby eath. But I'm going to
purposefully edit my gas fees to be super, super low so that the
node will actually not muck send the transaction to be mined, or
there are no miners that
will actually pick it up. So you can
see here I've divided the the priority fee and the max fee by
a ton. So it's super low. And it might confirm that in the MME
Max UI, you'll see that the transaction has been pending for
a bit. And we'll go over to this mempool tab. This is another
really useful visualization. And what the mempool is, is a kind
of a holding ground, I like to think of it as the waiting room
of a restaurant, where if you're a transaction, and you're
waiting to get mined, the me
mpool is kind of like the
waiting room where you're waiting to get seated. So there
are different statuses for your each of your transactions, the
ones that you always want to see are the mines transactions,
because that says that your transaction is successful. And
it's now part of the blockchain. Now, the mempool, every node has
its own, you know, holding ground. So I can actually show
you this quick visualization. Remember, blockchains are run by
a network of nodes. And each node or each comp
uter that's
running the theorem, software maintains a copy of the
blockchain. And as a developer, you have to use these nodes to
make requests to the blockchain. Now you can use alchemy, you can
use another RPC provider, you can spin up your own node if you
want to. But regardless, you need to use a node to
communicate with the chain. Now each node beyond having a copy
of the entire blockchain state, it also has a local memory of
transaction. And that's called mempool. So if there are pending
tr
ansactions that are waiting to be mined, you can consider them
as being in the mempool. Now that's what we're looking at
right here. If we click on the app that I am currently using
for my Metamask RPC, then you can see here that there are,
this is not the right one, this one is the right one for
wrinkling. For all the transactions here, you can see
some more drop in placed somewhere mind and there's one
that's pending, and this pending, one actually matches up
with the one that is pending, here
, it's being sent to 0x, C,
BB. And if we click on this transaction hash, you get all
the information that you need to debug. So you can see here that
it's from my current address, 0x, five F, and then it's two
0x, C, BB. And here's the value that I'm trying to send. Here's
the gas fee that I've attached to this transaction. And you'll
notice that that is super low, even for the Rinkeby test
network. So knowing this, and seeing, wow, this transaction
has been pending for one minute and 46 second
s, it was sent at
this time, I should probably fix that. And so over here, you can
actually use the metamath RPC, meta meta mask API. And speeded
up. And then I'm just going to use the auto high speed up to
update the gas fees. And then if we go back to our dashboard back
to our application, you can see that there are some new recent
invalid requests. And this is because we've resubmitted a
transaction, and then in their recent requests we have, let's
refresh that real quick. You can see that we
are sending a raw
transaction, this one's already known. And there's another one
before, but it that's resulting in a get transaction receipt
that is successful. And then if we go back to the mempool, you
can see boom, no more pending transactions only dropped and
replaced and mined. So this transaction nonce number five is
now successful, and you're on your way to developing and
maintaining the rest of your application. So yeah, thanks
hope that was useful. Let me know if you have any question
s. Now, other than the TypeScript
portion, which I'll do at the end, you've successfully
completed this section. And wow, you've learned a ton. Let's do a
quick review of everything that we've learned. Well, first,
we've learned how to create new projects with Node js, we've
learned what the node keyword does and how we can use the node
keyword. To run JavaScript in our local development
environment, we learned that we can add different dependencies
of external packages into our local package us
ing yarn or NPM.
And we can see those dependencies added in package
dot JSON, we know that they've been installed because they get
installed into the node modules folder, we can also create a
script section where we can minimize long commands that we
need to run into a single keyword, like compile for
example, we can just run yarn compile. To compile all of our
code, we learned the basic setup of our JavaScript scripts, we
import our packages at the top, we have some main executor
function at th
e bottom. And then we have our main function in the
middle, we use the async keyword so that our function can use
asynchronous programming, and we get access to the await keyword,
which basically means Hey, wait for this promise to finish doing
its thing. We're able to connect to any blockchain we want using
an RPC URL, and then we're able to connect our provider to a
wallet or a private key in ethers by doing something like
this. Speaking of So we've learned about the ethers
package, which is a
tool that makes our life a lot easier to
interact with the blockchain in JavaScript, if we decide to,
we've also learned we can encrypt our private keys so that
even if our computers get hacked, our private keys aren't
lying around in plaintext. And we've learned how to run scripts
from our encrypted keys. We've learned how to get the ABI or
the application binary interface, and the binary of our
code to deploy to a blockchain, we've learned how to deploy our
contracts to a blockchain programma
tically. And then we've
learned how to interact with our contracts programmatically as
well. Additionally, we've learned how to add a default
editor in our settings dot JSON of our VS code. But we've also
learned how to override those settings by adding prettier
using a dot prettier rc file this way, we can auto format our
code to make it look a lot nicer and much easier to read.
Finally, we learned how to deploy one of these contracts to
a real test net or a real network. And then we finally
le
arned the manual way to verify our contract source code. Like I
said, we're going to learn a lot of shortcuts. And a lot of ways
to make this all a little bit easier in coming sections. Oh,
you have done a phenomenally to reach this section, give
yourself a pat on the back, take a break, go for a lap, and feel
really proud about yourself that you made it this far, we've got
a lot more to go. But you have come a phenomenally long way.
Congratulations. Or take that five to 10 minute break and come
back when you're ready. Now the one thing left I want to
show you all is the TypeScript addition to this. However, if
you're not interested in the TypeScript edition, which you
don't have to be, then you're done, there's only a couple of
changes we need to make to make this TypeScript compatible.
First, of course, we're going to change our deploy.ts and encrypt
key, we're going to change our deploy and our encrypt key from
dot j s two.ts. And then we're also going to swap these
requires out for
imports. So we're going to import ethers
from ethers, we're going to import star as Fs from Fs extra.
And then we're going to import dot TMP slash config. And then
we're just going to copy these, and we're gonna come over and
paste them into here, deleting or commenting out the requires,
okay, great. Now, if we try to run Node deploy.ts, we're going
to get cannot use input statement outside of a module.
In JavaScript, if we'd want to use an import statement outside
of a module, we'd come in her
e and do some like type module
like that. But in TypeScript, we actually don't even need that.
All we need to do is run this in TypeScript note. So to add
TypeScript, we're going to do yarn, add TypeScript. And we're
also going to add TS node. TS node is the TypeScript edition
of node. So now that we've added that we can try a TS node
deploy.ts. And we're still going to run into an error. And if you
scroll up, we're gonna get a couple errors here. We're gonna
say, could not find a declaration fi
le for Module Fs
extra, we need to add the TypeScript version of them. So
we're gonna do yarn at types slash Fs extra. That, and if we
run it again, it still shouldn't work. But for a different
reason. Yes, we're gonna get something like this type.
Undefined is not assignable to type bytes. Like the reason we
get this is because process dot EMV private key in TypeScript
technically, is type string, or undefined. So we need to tell
Typescript and and the Wallet Objects. And the Encrypt
function i
s looking for a type string, not string or undefined.
So we just need to tell TypeScript that this will not be
undefined. So we can just put a bang here. And everywhere that
we use process, study and be, oops, I got to do that on
deploy, as well. Bang, looks good. Now that we've added
everything in here, we run TS node deploy.ts, we're gonna see
the exact same output as we saw with just using regular node.
And as long as our private key password is in our dot env file.
If we run TS node and cryp
t key.ts, we're gonna get the
exact same setup as before. And we're gonna get a new encrypted
key dot JSON. And that's all you need to do to make this
TypeScript compatible. And you should give yourself a huge
round of applause for getting this far and learning what's
going on underneath hardhat. The next tool that we're going to
learn and learning all about these transactions and how to
interact with these blockchains this is absolutely massive, so
huge. Congratulations. Alright, so now that we
've
learned about ethers js and how to do some more raw JavaScript
coding, we're now going to move into hardhat. We saw with our
ether symbol storage that deploying a contract can take a
lot of code. And there's a number of things we didn't even
do in here. Like we didn't save where this contract was
deployed. So we'd have to go remember where it was deployed
every time. Instead of having it just added programmatically. We
didn't write any tests here, and we'd have to build our own
testing infra
structure. Maybe we want to make this a cross chain
application. And we want more than just one private key and
RPC URL, you can absolutely work with your smart contracts in
JavaScript purely through ethers and small scripts like this. But
we want a more robust framework for doing all this. And that's
where hard hat comes into play. Hardhead is easily one of the
most, if not the most popular smart contract development
framework out there. It's used by massive several billion
dollar protocols lik
e Ave uniswap, sushi swap, and more.
In fact, I recently did a poll on Twitter. And even though a
lot of my content has been more brownie and Pythonic, Hardhead
was well and beyond the most popular framework, and Hardhead
has quickly become one of the most advanced frameworks out
there. Hardhead is a development environment, which allows for
JavaScript based development, kind of like what we saw with
ethers, it gives us even more tools to integrate our code with
common things that we want to do.
It's incredibly extensible,
and it has really nice debugging features as well. And it's just
an overall fantastic tool. So let's go ahead and let's jump
in. If you want to follow along with the code, come over to the
GitHub repo and scroll down. Lesson Six heart had simple
storage, and all the code is located here. And a quick note
for the future ever, you want to just download all the code from
one of these repositories, the way you can do that is by doing
a git clone, what you do is you come
to the folder that you want
to put this code in, and you run git clone. And then you grab the
URL that you want to clone, paste it in. Now, you can CD
into your new folder here that has everything downloaded
directly from GitHub, but only do that as a backup or to just
download the code yourself. But for now, just follow along with
me. Right, so let's do this. Let's create our next project
using hard hat. The project that we're going to be making is
called Hard Hat, simple storage dash FCC or Fr
ee Code Camp. This
is going to be us working again with that simple storage
contract. But in hard hat, we're going to show you a ton of the
fantastic tools that we can use to make our coding life way
easier. So I'm in a brand new VS code, and we're going to create
a new folder for us to run all this. Now what you can do to
create a brand new folder is you once again you can do MK dir,
hard hat, simple storage of CC. Now we can cd into hard hat
simple storage of CD, and type code, period. And thi
s will open
up a new Visual Studio code inside of that folder. Now if we
open up our terminal, you'll see that we are indeed inside that
folder. Now if that doesn't work for you, you can still of course
to File, Open folder and select the folder you'd like to open.
And you'll be inside of that folder. Now that we have our
folder setup for working with hard hat, we can begin setting
up our environment to be incredibly professional using
the hard hat framework got a link to the hard hat
documentat
ion inside our whole blockchain solidity course, yes,
the hard hat documentation is phenomenal. And I highly
recommend everybody have it up as they go through the section.
Because it's going to give you pretty much everything that you
need to know for working with hard hat, you can simply go
ahead over to tutorial and get started. If you want to pause
the video here and read through the tutorial, I recommend doing
so it'll give you a lot of information about how to work
with hardhat. And more ab
out hardhat. However, we're just
going to jump right into setting up the environment. We've
already installed no Gs on Linux, or Mac OS. And those of
you who are using Windows, I set you up with WsL. So you can just
follow the Linux instructions. Now to create a new hard hat
project, you can actually just go ahead and run these steps
right here. Instead of NPM, we're going to be using yarn,
but if you want to use NPM, you can absolutely do so the hard
hat Doc's say run npm init dash dash, yes, w
e're just going to
run yarn in it, which is going to create a new project for us
in this folder. So let's give it a name, which if we want it set
to this hard hat symbol storage FF FCC, we just hit enter, we'll
give it a version. And when if we want it 1.0 point oh, we just
hit enter. We're going to skip the description for now just by
hitting enter. And we're just going to hit Enter for this as
well. And for this as well enter for this as well and for this as
well and over this as well. Just to
keep those as blanks. And if
we look in package json, we now see we have a name Hardhead
symbol storage that FCC we have a version we have a main which
we're actually going to delete the main and then we have a
license as well. yarn in it just sets up this package json for
us. Now we're going to do yarn, add dash dash dash of our app. So far, we've just
been running yarn add, and then whatever our package name is.
But for most of what we're doing, we really want to do yarn
add dash dash Dev. Th
e reasons for this is a little bit
nuanced. But we can see some of the information on this
StackOverflow question here. The main difference is that
dependencies are required to run your project. Whereas dev
dependencies are required only to develop. For the most part,
we're going to be doing just dash dash Dev, when we get to
the front end portion of this course, we'll be installing more
packages that we don't need just for development. Now in the same
directory where we install hard hat, you ca
n run MPX hard hat.
So a quick note about MPX is that the yarn equivalent of NPM
is just yarn. So yarn goes NPM, the yarn equivalent of MPX is
also yarn. So pretty much anytime you see MPX, do
something, you can just replace that MPX with yarn, and it'll do
the exact same thing. If you want to run this with NPM or
MPX, you can absolutely do that as well. So for us, we're going
to run yarn hardhat. And we'll see we'll get prompted to
actually start creating a hardhat project, run yarn
hardhat. An
d we'll get this wonderfully cute prompt right
here and saying welcome to Hardhead. What do you want to
do, create a basic sample project, create an advanced
sample project can advance some project uses type script or
create an empty hardhat.config.js. For us, we're
just going to select create a basic sample project. And this
is going to give us all the boilerplate for a really simple
hardhat project. The hard hat project route is going to be
this folder that we're in right now. Do you want to a
dd a Git
dot Git ignore? Yes, we absolutely do. Because we're
going to be using dot env files. Do you want to install this
sample projects dependencies with yarn at nomic labs hard hat
at a theory and waffle at Chai? We're going to go ahead and say
yes, and I'll explain what all these dependencies are in a bit.
Let's go ahead and say yes for now. And we're going to install
all these dependencies. Now, if we look in our package, JSON, we
can see we've added a number of dependencies like nomic lab
s,
Hardhead ethers, not MacLeods, Hardhead, waffle, Chai,
Aetherium, waffle, and ethers. Obviously, we're already
familiar with ethers, but the rest of these might be a little
new. We'll talk about those later. And great, we now have a
sample hardhat boilerplate project. Let's walk through what
we just installed here. The first thing we have is a
contracts folder, which comes pre populated with greeted
outsole is really minimalistic contract here. Next, you'll see
node modules, which of course i
s our installed JavaScript
dependencies. Something I want to know because it was really
confusing to me when I first started working with this is
some of these node modules start with an add sign, and then a lot
of them don't. What's the difference between those two,
these outside node modules are known as scoped packages, which
effectively allow NPM packages to be namespace or yarn
packages. This allows organizations to make it clear
what packages are official and which ones are not. For exampl
e,
if a package has a scope at Angular, you know, it's
published by the Angular core team. So it's the same thing
with this anything with AP ens domains we know is by the ens
domains team, anything with abnormal Labs is going to be by
the team that created hard hat. So that's why this at nomic
labs, hard hat ethers and at nomic labs, hard at Waffle has
this at sign, because we know it's published by the nomic labs
team, then we have a scripts section. This is going to be
where we're adding any a
nd all of our scripts that we want to
write like deploying contracts, interacting with contracts, etc.
And then we have a test folder. We haven't started building any
tests yet. But tests are incredibly important for working
with smart contracts. And this sample test folder gives us a
minimalistic test for testing our smart contracts. We of
course have Git ignore, which of course comes pre populated with
some important things to ignore like Dotty and V. And also node
modules because node modules
might get too huge push up to
GitHub. And one of the biggest changes here is it adds this
hard hat.config.js. This file even though it's minimalistic,
right now, you can think of as the entry point for all the
scripts that we write, it's the configuration file that
determines how the rest of our code is going to work and
interact with the blockchain. Then of course, we have package
dot JSON, we get started with a readme. Remember how the first
time we ran yarn hard hat, we were prompted with th
is Getting
Started piece. Now if we run yarn Hardhead, we're actually
going to get output of all the different options and commands
we can use with running hard hat. Now, if
you run into an issue where you run yarn, hard hat, and this
pops up, but you don't see a hard hat.config.js in your
folder, it likely means that there's a hard hat dot config
dot j s in a higher level folder, or there's a node
modules with hard hat in a higher level folder. So if that
happens, maybe CD down a directory and
do a little LS and
look to see if you've got a hard hat.config.js or node modules in
earlier folder. And because I've actually seen a number of
engineers have a couple of different problems here. My
friend CAMI is going to explain A couple of different
troubleshooting tips you can take to try to avoid these
common errors. As a developer the most annoying
thing to deal with our environment set of issues. My
name is Camila Ramos. I'm a DevRel engineer at edge node
supporting the Graph Protocol. An
d I'm going to show you how to
solve two common problems that you might see when working on
this project. After installing hardhat. And running the command
and px hard hat in your new project folder, you're going to
expect to get back a menu of options like this, but sometimes
you're not going to get that back. And when you run into this
error, there is a solution for you. And it usually just means
that you have a config file somewhere that it shouldn't be,
and deleting it will get rid of that e
rror. What you're going to
do in order to find this file that you need to delete is run
the command MPX hardhats space, hyphen, hyphen verbose. And this
is going to spit out where this file is if you have one, and
it's going to tell you exactly where it is so that you can
delete it. After you've deleted this config file, you should be
able to run MPX hardhat in your project folder and get back that
many that we were expecting. Another problem that is pretty
common. And I still run into all the t
ime is forgetting to npm
install, whenever you're working with a repo that other people
have been working on on GitHub. So let's say you're pulling down
some code that you and some collaborators were working on
together, and then suddenly, it's not working for you, you
probably just need to npm install. So in your terminal, go
ahead and navigate to where this project is located and then run
the command npm install. If there are any new packages that
were installed in the time that you weren't wo
rking on the code,
those will get installed locally for you so that when you run the
code, it will be able to run successfully. What are some of the main things
we can do with hardhat in its raw state here, these are some
of the main tasks that we can run with hardhat different tasks
or just different commands, we can run with hardhat. For
example, we can do yarn hardhat counts, which will print out a
list of fake accounts we can use with Hardhead. Similar to the
list of fake accounts that we us
ed with ganache, we can
compile our contracts by running yarn Hardhead compile. Very
similar to what we did with ethers Jas and soap Jas, you'll
see when we run compile, we get a cache, which is just going to
be a quick way to access solidity files, and we also get
an artifacts section. This artifacts folder contains all
the information about our compiled code we look in here
now we can, for example, look in the build info and see a ton of
information about our compiled contract. If you're looki
ng
contracts, we can see more compiled information. And then
if we look in the hardhat, slash console.so, we can see more
compiled information. So all of our compliation information is
going to be in this artifacts folder. And whenever you want to
look to see what's going on on the lower level when you compile
this artifacts folder is what has everything. There are a
number of other hard hat tasks that we can run as well. But
we'll get to them as we go. So now that we have some of the
basics of
hard head down, let's go ahead and try doing some of
the same things we did with ethers before but with hard hat.
So one of the first things we want to do is we want to write
and interact with our smart contracts. So let's go ahead and
rename greeter dot soul to simple storage dot soul, you can
click on the file and hit enter and should be able to rename it.
Otherwise, you can go ahead and right click, delete it, and then
create a new file and call it simple storage dot soul. We're
going to copy
paste all of our code from our previous simple
storage that saw into this file, we can make sure that our simple
storage is compiling correctly by running yarn hardhat compile.
Whoa, it looks like we ran into an issue project cannot be
compiled. See reasons below the solidity pragma version of the
file. It doesn't match any of the configured compilers in your
config. Hmm, well, what's going on contracts slash symbol
storage that's Sol zero point 8.8. Ah, okay, let's go ahead
and fix them. So we
can open up our hard hat dot config dot j s.
Now a quick note on opening files. If you're on Mac and you
hit Command P, you can actually start typing in the names of
files to get them to them quicker. Or if you're on Linux
or Windows, you can type Ctrl p, this will bring up and
interestingly if you type man P or ctrl p and then you hit the
greater than key. This will drop you into the command palette,
know command palette, command palette, search for files,
search for commands in our Hardhead d
ot config. Jas, I'm
gonna scroll down to module dot exports and change this to zero
point 8.8. So that the version that we're going to compile for
simple storage, it's gonna be the same version and that hard
hat is looking for. Let's run that same command by just
hitting up yarn Hardhead compile, and awesome we see
compiled one solidity file successfully. We should now see
this in artifacts. If we go to artifacts and contracts. We now
see two contracts in here, greater and simple storage and
we
can see a ton of the information about simple storage
we can also see some more lower level in Information and build
info. Alright, so now that we have our simple storage contract
in here, the next thing we probably want to do is learn how
to deploy it. This is where we're going to write our deploy
script. Now, for this section, I'm going to be showing you how
to write a deploy script. But in the next section, we're going to
do it a little bit differently. But this is still going to teach
you ho
w to write scripts and worked with scripts in heart
app. So we're going to come to our sample script dot j s, and
we're gonna go ahead and hit enter and renamed it to deploy
dot j s. And if you want to read all the comments in here, you
absolutely can, we're just gonna go ahead and delete them all. A
quick keyboard shortcut is if you hit Command A or Ctrl, a,
you'll highlight all the text in your file, and we're going to go
ahead and delete it all. So now we're just going to start from
scratch h
ere. Now the setup for our deploy script in here is
going to look really similar to the setup of our deploy script.
From our previous section, we're going to do imports at the top,
we're going to have our async main function, and then we're
going to call the main function. So let's go ahead and define our
main function, we'll call it async. function main, like that,
and then we'll call our main function. And if you want to
just copy paste this from the last section, you absolutely
can. So we'll
domain that then. Boom, just like that. And
because of these semicolons are going to drive me absolutely
insane. We're also going to add prettier, and our solidity
prettier plugins. So we'll do yarn, add dash dash Dev,
prettier and prettier, plug in solidity. Then we can go ahead
and create our dot prettier, rc file, we're going to add tab
with four, use tabs, false, semi false, and then single quote,
also false. Now we're going to be using this prettier rc file
setup a lot. So in future section
s, if you want to just
copy paste it, you can absolutely do that as well.
We're also going to add a dot prettier, ignore, which tells
prettier not to format some files, which we want, we don't
want prettier to spend a ton of time formatting all of our
files, I'm just going to copy paste from the GitHub repo. So
feel free to copy paste from the GitHub repo as well, you'd find
all the code for this section, like I said, in the GitHub repo
associated with this course. Now, unlike in our last sectio
n,
where we had to grab our contract code a little bit more
manually, with hard hat, it's actually a number of different
ways to grab compiled contracts. The first way we're going to do
it, we're actually going to use ethers. And now this is where
one of the first confusing changes actually comes in.
Previously, we did const ethers equals require ethers. And that
was how we went ahead and worked with ethers. However, you'll
notice in our dev dependencies, we have this dependency called
Hard Hat
ethers. Hard Hat ethers is a package that actually wraps
hard hat with its own built in ethers. This is really
advantageous because it allows hard hat to keep track of
different deployments at different scripts and all these
other things for us. So instead of importing ethers directly
from ethers, we're actually going to import ethers directly
from hard hat instead. This might seem a little confusing at
first, but just know if we want to work with ethers and heart
it, it's usually much better to
pull it in from heart and you
can still do this and ethers will still work the same. But
hard hat won't necessarily know about different contract
factories in different pieces and, and you'll see that in
action in a second. Now that we're pulling in ethers, we can
actually immediately grab a contract factory using ethers.
We can say const simple storage factory equals await ethers dot
get contract. Factory simple storage. So in order to get a
simple storage, contract factory, we can just do awa
it
ethers dot get contract factory. Now if we pulled right from
ethers, the package ethers doesn't know about this
contracts folder and ethers doesn't know we've already
compiled simple storage dot soul. And it's in our artifacts.
Hard Hat, on the other hand, does know about the contracts
folder and does know that it's already compiled, which is why
this simple storage factory grabbing work so well. Once we
have our factory here, we can do the same thing that we did in
our previous section and d
eploy the contract. So we'll do a
quick console dot log. Deploy flooring, contract done, and
then we'll do Kant's simple storage equals await simple
store edge factory dot deploy And boom, with that little bit
of code, we're already able to deploy our simple storage
contract, then to wait to make sure it gets deployed, we can do
await, simple storage, deployed. And that's it. Now let's see
what happens when we go ahead and run this deploy script. As
you know, in our last section, we had to put i
n a private key,
and we had to put an RPC URL. Right now, we don't have either
one of those defined. So what do you think should the script
actually work? Or do you think it'll break because we, we
didn't define what blockchain we're going to deploy you. We
also didn't find a private key. Well, let's go ahead and try
this out. We can run the script in our terminal by running yarn,
hard hat, run, scripts slash deploy dot j, s. And again, I'm
hitting tab here to do a little auto completion and see
what
happens. Well, we got deploying contract, it says done, but
that's really it. So what really happened? Well, let's add one
more line in here. Let's do console dot log. We'll
do some string interpolation deployed, contract to. And then
we'll add simple storage. That address. Let's run this now. We
get deployed contract, and then we get deployed contract to, and
then we get a contract address. Hmm, what's going on here.
Hardhead has this fantastic tool built in called The Hard Hat
Network ha
rd hat comes built in with Hard Hat Network, a local
Aetherium network node designed for development, akin to
ganache, that allows you to deploy your contracts and run
your tests and debug your code. Whenever we run a command in
hard hat, or a script and hard hat or a task and hard hat, we
by default deploy to this fake Hard Hat Network. This Hard Hat
Network is very similar to ganache, except for instead of
having this UI, it runs in the background for our scripts. In
fact, if we go to our hard
hat.config.js, we can scroll
down to the bottom to this module that exports section and
add more information about our default networks. So right now,
if we don't have anything in this module that export, by
default, it adds this piece called default network hardhat.
So anytime we run a script, without specifying a network, it
automatically uses this fake Hard Hat Network. And this fake
Hard Hat Network comes automatically with an RPC URL
and a private key for you. So you don't even have to add
one
in. This is one of the major advantages of working with
Hardhead. It just automatically gives you this fake blockchain.
And these fake private keys, if you want to be a little bit more
explicit. And I always recommend being more explicit, we can add
the default network in to the module that are experts. So now
our default network is explicitly stated as hard hat.
However, in any script, you run, you can choose whatever network
you want to work with. So if I want to explicitly say I want to
run our deploy script on our fake Hard Hat Network, I can do
yarn, hard hat, run scripts, deploy dot j s, dash dash
network, hard hat, this is us telling her that, hey, we want
to run this script on the Hard Hat Network. Hopefully, you
might be able to see where this is going. Having this network
flag makes it incredibly easy to switch across different chains,
different block chains, different private keys, etc. So
we have our default network set the hard hat here, we can add
other networks in h
ere as well. The way we do that is we're
going to add a networks section. And we're going to define any of
the network sections that we want. And remember to put a
comment there so that your Visual Studio code doesn't get
mad at you. So recently, we worked with Rigby so let's go
ahead and add a Rinkeby network in here. So we're gonna say
another network is gonna be really cool. So I should just be
able to change the network flag to Rigby now, right? Well, not
quite. If you try to run that, you'r
e gonna get invalid value
undefined for hardhat config networks. Rigby, that URL, it's
expecting you to tell it, hey, what the URL is, since this
isn't the hard hat network, we need to tell hard hat exactly
how we're going to connect to rink B. And this is where a lot
of what we learned before is going to come in handy, again,
exactly the same as what we did before. We're going to create a
new Dotty v file, and we're going to add our Rigby URL in
this dot env file. Just remember, dot env is an r
dot
get ignore just in case. So in our Dotty env, we're going to
add that RPC URL from alcmi back in here before we just said RPC
URL. But since we might want to work across multiple networks,
it's usually good to specify exactly what network each URL
stands for. So we're gonna say Rynkeby RPC URL equals and then
paste that URL in here. Now, as you probably have guessed, we
can add our URL to our Rinkeby network here. for readability, I
usually like to add them as variables right above the modu
le
that experts. So I'll say const Rynkeby. RPC URL equals process
dot E and V dot Rynkeby RPC URL. And once again, we're going to
be pulling that Rinckey RPC URL from our environment variable.
Of course, in order to pull that environment variable in, we're
going to need to use that dot env package again. So to add
that in, we're going to yarn add dash dash dev dot EMV. And at
the top of our Hardhead config, we're going to add require dot
EMV, and then do dot config to enable the config. Now thi
s
means we should be able to pull our rink the RPC URL from our
dot env. Now that we have that in our Rinkeby network, we can
add URL Rinckey RPC URL. Awesome. So we have an RPC URL
for different network. But what else do we usually need? Well,
we usually need a private key to work with an actual network, or
that doesn't automatically give us a private key for rugby,
because Hardhead can't just give us test and the Etherium, we
need to have an actual account an actual test nets. Hardhead
doesn't
control those. So we have to actually give it a real
URL and a real private key. So to add private keys, you
actually add something called accounts, you add a list of
accounts that you want to give to hard hat for us, we're only
going to add one, which is going to be our private key. And for
our private key, we're going to do the exact same thing. We're
gonna say const private key equals process dot e NV dot
private key. And since this private key is going to be a
real private key for a real te
st net, again, we are going to have
to grab this from our meta mask. So it will go to our meta mask,
three dots, account details, export private key, and we'll
add our password in here. And then in our dot env will add
private key equals and then add our private key. Now I know I've
said this 100 times, but please, please, please, for learning
this, do not use a real key that is connected to any real money
just in case, please use a new meta mask. I've know I've said
it a bunch. But some people
go no, I'm going to be okay, I'll
be safe. Just to be super, super safe here, please use a brand
new meta mask. So now that we have a private key, we're going
to add it in here account private key. And now we have an
account here. One more thing I like to do is I like to give the
chain ID of the network, which for Rigby is going to be for
every single EVM base network as a new chain ID and EVM network
basically just means solidity works on it. This includes all
test nets, there's a good site cal
led chainless.org, which may
or may not be going down at some point, it has a list of all
these different networks. For example, you can see on here a
theory main net has a chain ID of one byte and smart chain is
56, avalanches, 4311 for Phantom, opera 250, Polygon,
137, etc. Each one of these EVM compatible chains has their own
chain ID, rank V, the chain ID is for adding the chain ID is
helpful here for later on. And we'll get to that in the future.
But for now, just go ahead and make sure to
add your chain IDs.
Okay, now that we have the RPC URL, we have the private key, we
can go ahead and test deploying this to an actual test net, and
actually did something incorrect here. And we're gonna get an
error here. And I want you to go ahead and try to figure out and
debug this error yourself. You're ready. Alright, let's do
it. We'll do yarn, hard hat, run scripts, deploy dot j s, dash
dash network Rinkeby. And we get this wonderfully weird error,
which we see we have deployed contract.
So we know that in our
deploy script, we get to at least this line, but then we're
getting an error, I highly likely hear what's going on
saying cannot read properties have no reading send
transaction, if you want, you can absolutely go to this spot.
But it basically it looks like it's having a hard time
understanding what the private key or what the account of this
is. And what do you think I'm going to recommend we do? Well,
if it's not clear, after doing a little bit of triaging and
debugging
, we're going to copy this air. And we're going to
come on over to Google and paste that right in. It looks like we
do get a question here from stackexchange Etherium. And it
looks like it's really similar to what we're doing. We scroll
down. They're running nearly the exact same script that we're
running, they're using MPX instead of yarn. They've got a
pretty minimalistic deploy file. Let's scroll down and see what
the answers have to say. I've seen this error where my private
key wasn't prope
rly populated. I would also use an environment
variable I'm pretty sure Are environment variables good. But
we have a second one saying in your heart head, I can think
that Jas, it should be accounts instead of account, it works for
me, let's go back to our head config and see if that's what's
going on. Uh huh, we put account in this should be accounts. So
let's swap that over to accounts. We'll clear our
terminal. And we'll run this again. Uh huh. And now it's
reading a little bit longer, which
is good. This means that
we're probably deploying this to rink B, which is what we want to
see. Awesome. And now we can see deployed contract to, and we
have a contract address here. So we'll grab this contract. And
we'll pop on over to bring the ether scan, that's numbering the
ether scan. And we'll go ahead and we'll paste this in.
Awesome. And we see our contract was created about 26 seconds
ago. Perfect. Now for this part, you don't have to deploy this to
rink be with me, if you follow alon
g here. That's good enough.
So remember, the flying to test that's can take a long time. So
for this one, you don't have to deploy with me. Alright, great.
So we've deployed to rink be using hard hat. This is
fantastic. Now something that we notice, once again is oof, our
contract isn't verified? Do we have to go back through and do
this verify and publish and all that stuff again? Luckily for
us, we actually don't need to do that. So what can we do? Well,
back in our deploy script, we can add s
ome code to
automatically verify right after we deploy. So let's go ahead and
do that. Right below our main function, we're gonna create a
new function called verify, we're gonna say async function,
verify. And we're gonna have this function get past some
arguments, we're gonna have to get past a contract, address,
and some arguments or the contract. Since our simple
storage doesn't have a constructor, the arguments for
simple storage are just gonna be blank. But in the future, when
we have cont
racts that do have constructors, the arguments are
going to be populated. And when we get there, you'll see what I
mean, we need at least the contract address, and we're
going to add some code in here to automatically verify our
contracts after they've been deployed. This auto verification
process works on block explorers like ether scan, it might not
work on block explorers like eath, pler, or other block
explorers. But if you want to verify on these other block
explorers, I'm sure they have an
API to allow you to do that as
well. Now, ether scan in most other block explorers have a
section on their website called API documentation, or something
to do with API's. These are ways for us to programmatically
interact with ether scan, and do stuff with them. One of the main
things that we can do is we can actually verify our contracts
through this API. Ether scan even has a tutorial in here
called verifying contracts programmatically. And the link
to this will be in the GitHub repo, they h
ave an API endpoint
that we can make some requests to to go ahead and verify our
contracts. Now, we could absolutely make the raw API
calls and follow the tutorial here. But there's actually an
easier way than even going through this tutorial here.
Hardhead is an extensible framework, meaning you can add
something called plugins to it. There's even an advanced section
in the documentation called Building plugins. If we scroll
down to the bottom, we can see some popular plugins that the
nomic lab
s team or the Hardhead team has created. And also a
number of community plugins as well. One of the most used
Hardhead plugins is going to be this hard hat ether scan plugin
that makes this verification process much, much easier.
Install it, you can just run npm install bash, just save dev at
nomic labs harden ether scan, and then add it to our hard hat
dot config. Since we're using yarn, we're just going to go
ahead and use yarn. So back in our code, we'll do yarn, add
dash dash Dev, at nomic,
labs, slash hard hat slash hyphen,
ether scan. Now that we have this plugin, we can go to our
hard hat dot config, scroll to the top and add this plugin do
require at nomic labs slash hard hat, ether scan. Now that we
have this plugin, the hard hat documentation has some more
information about the usage, how to actually use this plugin, and
how to run different commands with it. In order for us to use
this verification, we actually need an API key from ether scan.
This is basically a password fo
r allowing us to use the ether
scan API. So we're going to come to ether scan. And we're going
to go ahead and sign in. And actually we're going to click to
sign up and create an account. And we'll go ahead and create an
account. We'll go ahead and verify our registration by
clicking the verification link. And we'll click the Login. Now
that we're logged in. On the left hand side, we can scroll
down to API keys. And we can go ahead and create a new API key.
We call this H H hyphen sec, which sta
nds for hardhat Free
Code Camp. Great, this new API key will copy this and we'll go
back to our code and we'll add this somewhere since the API
keys basically considered a password. Where do you think we
should add this? That's right in our dot env. So in our dot env,
we're going to add a new entry called ether scan API key. And
we're going to add that API key that we just got. Now that we
have our API key, back in our Hardhead, config, we're going to
create a new section in a module dot exports
, tell hardhat that
we have this ether scan API key, or new section is going to be
called ether scan. And in here, we're gonna say API
key is going to be ether scan API key that we're going to
define up here the same way we define these other keys. So
we'll say const, ether scan API key equals process dot e NV dot
ether scan API key. And if something like this pops up, you
can generally just hit enter, and it will autocomplete it for
you, which is awesome. Great. So now we have an ether scan API
key back in the heart had documentation, it tells us by
adding this, we actually get a new task called verify. Let's
try that out. So open our terminal back up. And we'll do
yarn, RT hat. Let's see what pops up. Wow, we did get a new
verification here. When we run yarn hard hat harder, it
actually looks into our hard hat.config.js and checks for any
plugins. If there are new plugins there, it'll add them as
a new task that we can do. You can manually verify your
contract by doing yarn or MPX Ha
rnett verify dash dash
network, the deployed contract address and any constructor
arguments yourself. But we want to be a little bit more
programmatic than this. So what we're going to do is we're going
to go back and create this verification function, it is
good to know how to do it via command line so that if you want
to verify something in the future, manually, you can, let's
build this verify function though. So we're going to take
the our contract address and some arguments. And for our
sak
e, we're going to do console dot log verifying contract that
that just so that we know we might have to wait for a little
bit. And in our code, we can actually run any task from hard
hat using a run package. So up at the top, we're actually going
to import run from hard hat as well run allows us to run any
hard hat task. So in our code here, we're going to do a wait
to run. And then we can do there it thought. Now part of that
allows you to add different parameters as well in this run.
And it's
usually best that you go ahead and add them in here so
that we're really specific with what we're doing. If we do yarn
hardhat verify dash dash help, we can see what parameters we
can actually pass. Well, it looks like we can pass the
Verify parameter. So we'll do colon, verify. If you go to the
actual GitHub for the verification tasks, you can
actually see you can do more than just verify, you can do
verify, get minimum build, verify, get constructor
arguments, verify, verify, which is what we'
re going to be
working with, and a couple of other subtasks as well. The
second parameter that goes inside run is going to be a list
of actual parameters. This second parameter here is just
kind of the sub task, if you will, of our verify task. And
this is going to be an object that contains the actual
parameters. And this is where we pass in an address, which is
going to be our contract address, and then our construct
or arguments, which is going to be arcs. Now normally just this
right here sh
ould be enough for us to go ahead and use this
verify contract in our main function, but we're going to add
one additional thing to it. Because in practice, sometimes
there's some errors that can come up. One of the errors that
often comes up when running a wait is that the contract has
already been verified. And you'll actually likely run into
this, because ether scan will get smart enough by seeing
enough bytecode that is exactly simple storage that it will
start to just automatically verify a
ny bytecode that looks
like simple storage. And then this await will throw an error,
which we want to avoid. So what we can do is we can add a try
catch onto this await. So outside of the way, we're going
to add a try. And we're going to add these little brackets that
wrap around our weight. And then we're going to put a catch. This
is known as a try, catch and solidity also has tried catches.
But basically, this e is going to be any error that this
section throws. So we're going to do is we're
going to say if
this message is already verified, then we're just going
to continue. So we're gonna say if e.message.to lowercase, we're
going to make sure it's to lowercase that includes already
there. If five, then we're just going to console dot log already
verified like that. Otherwise, we're just
going to console dot log e. The reason we do This is because of
this errors, our verification function will break, and our
whole script will end. And we don't want our whole script to
end, we want
our script to keep continuing if the verification
doesn't work, because it's not really a big deal. So I know
this might seem like a lot of code, feel free to copy and
paste it from the GitHub repo to just move along. But awesome. So
we now have a verify function using the Verify task in
hardhat. Let's go ahead and use this now in our main function,
right below our deploy, we'll do console dot log deployed
contract two, and then the contract address. But before we
call this main function, let's
think for a quick second. What
happens when we deploy to our RT Hat Network? Well, remember, if
we deploy to our Hard Hat Network, will our contract need
to be verified I need to scan? Well, we know there's a we know
there's a rink ether scan, we know there's a COVID ether scan,
we know there's a main net easy scan. But is there a Hardhead
ether scan? No, of course not. Right? The hard at runtime
environment is a network local to our machine. So it doesn't
make sense for us to verify a hard hat
network deployed
contract on ether scan. So we actually don't want to call this
verify function, when we're working with our local network.
This is where these chain IDs are going to come in quite
useful. What we can do is we can check to see if the network that
we're running on is a live network or it's a test net, or
it's a network that actually can be verified, we can actually get
network configuration information by importing a
network like this. And we can do something like console dot log
network dot config. Now, if I run yarn, art, hit run scripts
deploy.js. On our Hard Hat Network, since I'm not passing a
network flag, we get this massive output that looks like
this. Our network dot config contains a ton of information
about the current network that we're on, you'll see here that
the chain ID of the Hardhead network is actually 31337 gas
price, which gets set to auto block gas limit the current fork
of Aetherium that we're working with, and all these other pieces
here. This cha
in ID is really important. Because we can use
this chain ID to figure out which one is a test net, or
which one is a live network. And remember, running the script is
going to be the same as doing dash dash network, hard hat, you'll see our channel ID is
still 31337. Again, that's because the default network in
our heart had config, it's hard hat, which is the same as saying
every single time we run a script, we're secretly running
it with dash dash network hard hat. So now we only want to
verif
y on our test net. So what we can do is we can say if
network dot config, that chain ID equals equals equals four,
which is going to be rank B, ad. And a JavaScript equals equals
equals is nearly the same as equals equals, except no type
conversion is done, which just means in JavaScript, four equals
four, and four equals equals the string of four, but four, but if
you were to use four equals equals equals four, this is
false. This is true equals equals four equals equals would
be true, four equ
als equals to the string of four would also be
true, but four equals equals equals to the string of four is
going to be false. So you can kind of do whatever you want
here equals equals or equals equals equals. So we want to say
if the network dot config dot Trinity is four, so if we're on
Rinkeby, then we can go ahead and actually verify. But we also
want to make sure we only verify if our ether scan API key
exists. So we can also in here is site and this double
ampersand means and we can say p
rocess dot e NV dot ether scan
API key. This is some Boolean tricks that we're doing here,
basically, so our first conditional we're saying if
network dot config chinati equals equals equals four, this
section can be true or false, obviously, right? The chain ID
that we're running on can be the hardest network, which would
mean this doesn't equal four, or it would be Rinckey, which means
this does equal four. But there's no conditional on this
side. So how does this side work? In JavaScript, if
an
object exists, and you try to cast it as a Boolean, it will be
converted to true. If it doesn't exist, it will be converted to
false. So in JavaScript, basically, if either scan API
key exists, if we have this in our data in VI, this will be
true. And if not, this will be false. So another way to read
this line here is saying if network dot config that Trinity
is for AKA, if we're on rink B, and our ether scan API key
exists, then do some stuff. And that's what we're going to do
here. So in h
ere, we'd want to run verify, or verify function
and pass it the contract address which is going to be Simple
Storage dot address and the constructor arguments which we
know are going to be blank. And since our verify function is an
async function, and it deals with promises and stuff, we want
to add the await keyword here. Awesome. So we've added a way to
actually verify our contract. But we're not quite done. See on
ether scan and all these block explorers the instant we deploy
the contract an
d the instant we send the contract, ether scan
might not know about the transaction yet, it might take a
hot second for ether scan to be up to speed with where the
blockchain is. So it's usually best practice to wait for a few
blocks to be mined, until you actually run your verification
process. We've actually learned how to do this already with the
deploy transaction. So before we actually verify we run, we want
to run a weight, simple storage. Deploy transaction Wait, six. So
we will wait six
blocks, and then we'll run our verification
process. Now, if you want to go and test this out right now, you
absolutely can. I'm going to keep going, though, because
again, testing all these on a test net takes a little bit of
extra time. So I'm going to finish the rest of our main
function, and then I'm going to run everything all together.
Okay, cool. So we've deployed our contract, we've
automatically programmatically verified our contract. What's
next? Well, what did we do last time, we star
ted interacting
with the contract. So let's do const. Current Value equals
await simple storage dot retrieve. To get the current
value, simple storage, that's all. We have a retrieve
function, which returns the favorite number, so let's get
the current value. And we'll do console dot log, the current
value is and then some string interpolation, current value.
And then we'll go ahead and update the current value by
doing cons. Transaction response equals await simple storage,
that store will stor
e the number seven, and then we'll await
transaction response dot Wait, we'll wait one block for that
transaction to go through. And we'll grab the updated value by
saying const updated value equals await simple storage dot retrieve. Then we'll do
console log. The added value is updated value. Awesome. And this
is going to be our whole script. So if I can zoom out for a
little bit, I know it will be a little bit small here, we've got
this huge main function, which does what? Well it deploys our
contract. If we're on a test net, it then verifies our
contract. And then it updates the value to seven. And we have
our verify function down here. And we have a section of our
code that calls our main function. Now for run this on
the hard hat network. What do you think will happen? Well,
let's try yarn, or net run scripts. Deploy dot j s.
Alright, awesome, we get exactly what we saw before we get
deploying contract deployed contract to current value is
zero, update value seven. And there's not
hing in here about
verification. That's exactly what we want. Now, moment of
truth. Let's try this on Rinkeby. We'll do yarn, RT hat
run scripts, deploy dot j s, dash dash network Rinkeby. And
it's gonna go a lot slower, because obviously now we're
deploying to an actual test net, where the blocks actually need
to be mined. And we see we haven't deployed the contract.
Now that our contract is deployed. We know that we're
currently waiting six block confirmations for us to go ahead
and verify. An
d actually I should add console dot log,
waiting for block the x's so that we don't get campus weird.
Oh, wait, what are we doing now? That it looks like we ran into
this error, no such file or directory, it looks like our
code might not have compiled correctly here. So here's what
I'm gonna do. We're gonna go ahead and delete our artifacts
to trash. We're gonna delete our cache as well. And we're going
to try rerunning this whenever you run a script with the hard
drive command Hardhead will aut
omatically recompile it for
you, especially if there's no artifacts folder. So we're going
to rerun this command and heartbeat, it's going to compile
first and we can see that it does exactly that. And then it's
going to go ahead and redeploy. Hopefully this time, it should
be able to find the contract that had just compiled looks
like this time after I deleted the artifacts folder, we
actually did indeed get some successful compilation. And we
can see here successfully submitted source code for
contract or verification on the block explore waiting for
verification results, successfully verified contracts
simple storage on ether scan, and even gives us a link that we
can go ahead and Command click or control click into. And we
can see the contract indeed being verified. This is awesome.
This is perfect. We've now got a successful deploy dot j s script
that can deploy, verify, and then interact with our code.
This is fantastic. This hard hat thing seems pretty cool. What
else can we do
with hard hat like I showed you before. Hard
Hat comes with these tasks. And the number of tasks that
Hardhead can come with can be extended by us writing plugins,
we can actually write our own tasks in hardhat. And in our
Hardhead dot config, it can defaulted with this task
account, we can see task accounts, prints the list of
accounts, and just prints a list of accounts here actually go to
the heart had documentation to learn more about creating our
own tasks, one of the ways that you can defi
ne tasks is directly
in our heart head.config.js. But typically, what people do is
they have a new folder called tasks where they put all their
tasks. So for now, I'm gonna go ahead and delete this section
here. And we're going to create our own task. You'll notice that
now that we've deleted that section, if we run yarn RDAP, we
no longer see the accounts task in here, because we've just
deleted that task. So let's create our own new task. We'll
call this block number.js. And we'll use this to
get the
current block number or whatever blockchain that we're working
with. So let's create this task. First, we need to import the
task function, we can get it by saying const task equals require
arhat slash config. The hard hat slash config has the task
function. To define a task, we can now just say task, give it a
name and a description, the name is going to be blocked number. And then the description is
going to be prints the current block number. Now that we have
this task, there's a coup
le of things we can actually do with
it, we can add different parameters to it by using the
dot add command, which allow us to pass parameters to the task.
And then we could also set actions which define what the
task should actually do for us, we're just going to do dot set
action. And define what we want this function to do. So we're
going to make this an async function, that's going to take
as an input, the task arguments, which are going to be blank for
us, and the HR E, which I'll define in
a second. Now, let me
explain the syntax really quickly. This might look a
little bit weird, but this is what's known as a JavaScript
arrow function. In JavaScript, you can actually define
functions without even using the function keyword. For example,
if we go back to our deploy function, we have our async
function verify down here. However, another way we could
have defined this is is without using the function word at all,
and actually turning this whole thing into a variable, we could
have
said instead, we could say const verify is going to be an
async function that takes contract addresses and
arguments. And here's the function definition. These two
lines are essentially equivalent. There's some slight
differences between between using the function keyword and
having your function be a variable. But for the purposes
of this course, they're basically the same. Which means
though, that this by itself is a function, just not assigned to a
variable. But essentially, the two of these
do are exactly the
same. And that's the syntax that we're doing here. You can
imagine this sort of being like const block task equals async
function, which takes the params and then runs that arrow
function. Or you can think of it as async. Function, block task
parameters, and then the function definition, these are
all essentially the same. The major difference is that we're
never giving our function a name, we never give it this
block task variable. This is known as an anonymous function
in Ja
vaScript, because it doesn't have a name. Now that we
have our function in here, we can now call some function to
get the block number. Well, how can we get the block number when
we run tasks, we automatically pass our anonymous functions,
the task arguments, which in this one, we don't have any, but
we also pass this HR II object. This HRV is the heart hat
runtime environment. Back in our deploy script, this is basically
the same as this require hard hat in here. So this HRV can
access a lot of
the same, this HRV can access a lot of the same
packages that the hard hat package can. So we can do Hae
dot ethers, just like how you can import ethers from hard hat.
And in our ethers package, there's actually a number of
functions we can use like dot Pro, biter dot get block number.
Let's save this to a variable const blocked number equals, and
this is going to be a synchronous, so we're going to
want to add a weight here. And then let's do console dot log
block number. Or better yet, we'll
string interpolate this
and say current block number like this. Now though, if I try
to run this task, you'll notice it doesn't show up in the heart
Atlas of tasks, let's do yarn. Art app. Hmm, I don't see block
number in here. Well, this is because we need to add it to our
config dot config will add require dot slash tasks slash
block number. And in order for us to import
it, and let's add a module that exports I'll explain what this
does a little bit later. But now that we've required it, if I
run
yarn hard hat, and now see blocked over is one of the tasks
that I can use. Now for a run yarn, art hat block number, we
get current block number is zero. And this makes sense
because this is defaulting to our Hard Hat Network, which gets
reset every time we run it. But if I run yarn, hard hat block
number A dash dash network Rinkeby, what do you think I'm
gonna get? Again, a much larger number, current block number is
right here. Because this is the actual block number of Rigby
versus the
block number of our heart ad network is going to be
zero because it gets reset every single time, we run one of these
scripts now scripts and tasks both can basically do the same
thing. They both can interact with contracts, they both can
deploy smart contracts, they can both pretty much do everything.
I prefer scripts just as a general rule of thumb, because I
don't always think adding a special thing from the command
line makes sense. So I prefer scripts, but you'll see a ton of
tasks and exam
ples out there as well. I think tasks are really
nice for specific use cases. But for the most part, we're pretty
much going to use exclusively scripts, but it is good to know
what a task looks like and how to use it. I think tasks are
better for plugins and scripts are better for your own local
development environment. But if you want to do everything with
tasks, you absolutely can. As you're starting to see, this
config piece is pretty powerful. And we can use it to modify our
entire code base
and our entire project to give our project more
functionality. What else can this do? Well, right now, as you
can see, every time we work with a hard hat network, every time
we run a script, we run that script, and then the Hard Hat
Network is deleted, right? We can't interact with our
contracts anymore. Well, there's actually a way for us to run a
hard hat network. Similar to how we ran a ganache network with a
user interface. What we can do in hard hat is run yarn, RT hat
node. And what this
will do is it'll spin up a node on a local
network, exactly the same as ganache but in our terminal. So
you see here started HTTP and WebSocket. JSON RPC server at
this address. And just like nosh, it comes packed with all
these different accounts and private keys, which is awesome.
You'll notice though, interestingly enough that this
node that we're running isn't on the Hard Hat Network, well, we
can actually create a new terminal to try to interact with
this just by hitting this little plus bu
tton and creating a new
terminal. Again, I'm using bash, but based off of whatever your
operating system is, you can be using a different shell. And
here, let's go ahead and run yarn arhat run scripts, deploy
dot j s, and see what happens. Well, our typical setup happens,
we deploy a contract, we get a contract address, we update the
value. But if we look at our node, it doesn't look like any
transactions went through what we don't see any locking here.
So what's going on? Well, our Hard Hat Net
work is actually
different from this locally running network here. This
locally running network we often want to refer to as our local
host. So it's slightly different than the Hard Hat Network. It's
still using the hard hat runtime environment, but it's just not
this default Hard Hat Network. It's considered its own separate
network when we're running a node that it's going to live. of
past the duration of a script. So we can actually interact with
this by adding a new network to our hardhead.c
onfig.js, we'll
create a new network and call it local host. And exactly as we did up here,
we'll give it a URL accounts and a chain ID. So for URL, we can
get that URL right from our terminal, I'll put a running
yarn hard hat node. by copying that and pasting it in here, we
can then do a comma, we'll give it a chain ID of 31337. Because
even though it's considered a different network, it actually
has the same chain ID as hard hat. Interestingly enough, I
know I just said we were going to give i
t accounts, but we
actually don't need to give it accounts. Because when we run
with this localhost hard hat will automatically give us these
10 fake accounts for us. So you can kind of think of the
accounts here for this localhost as, as hard had already placing
them in thanks, hard hat. But now, if we go back to our bash
here, let's clear the terminal. Let's rerun the script. And
we'll do dash dash network local host. Now we should be pointing
to this node. And when I run this script, we shoul
d see some
logging output at the end of this node. So let's go ahead and
hit enter. Well, we see our normal stuff on our deploy
script, we flip back to our node Wow, we see a ton of logging
here. Similar to ganache, we can see a whole bunch of different
logs on what just happened, we can see a contract was deployed,
we can see the address the transaction hash from value,
gas, and the block number and everything. We can also see our
contract call calling the store function to update the value of
our favorite number. This is incredibly powerful for quickly
testing and working with things on a local JavaScript VM or hard
hat network to be able to see how your contracts will interact
on a real test net. And this is much quicker than working with a
real test net. Now additionally, same as what we said before, any
process that's running in the terminal, we can kill it with
Ctrl C. So if you want to stop your node and then restart it,
you can hit Ctrl. C, to stop it, and then just up and then
rerun
that same command to re spin up your node Ctrl C stops it. And
then you can up to start again. Another way you can stop it, of
course, is if you hit the trashcan, which deletes the
whole terminal, we pulled a terminal back up, we can run it
again. And just remember, if you hit the X that actually doesn't
delete the terminal, that just hides it. So our hard hat node
right now is still running because I just hit it. So I pull
it back up, I can see that it is indeed still running. But if I
t
rashed candidate, and then it pulled the terminal back up, I
can see that it is no longer running. So running scripts is
great. But what if I don't want to have to code an entire script
to do some things? What if I want to just tinker around with
the blockchain? Well, hard hat comes packed with this thing
called the console. The console is a JavaScript environment. For
us to run JavaScript commands to interact with any blockchain, we
can jump to the console by running yarn, hardhat, console,
and
then whatever network flag if we want to work on rink B,
main net polygon, avalanche, etc. That's just network local
host. And now we're dropped into a shell him in the shell, we can
do everything that we do in a deploy script. And we don't even
have to run these imports, because everything with hard hat
is automatically imported into our console. So for example,
let's say I wanted to get a simple storage contract factory.
Well, I could run exactly this line here. I could say const,
simple stor
age, factory equals await ethers dot get contract
factory, have simple storage. And now I can go ahead and even
deploy this. So I can even just copy this line, paste it. And if
we flip back to our node, we'll see that we just deployed a
nother simple storage. And now we can do things like await
simple storage dot retrieve. And I get the return value which is
going to be a big number with a value of zero. I can also make
transactions so I can do a weight, simple storage. That
store let's do 55 If
I hit up twice, I can go back to the
simple storage dot retrieve. call that function and I can see
my big number has a value of 55. Now, this is a great way to
quickly interact with any blockchain that we want. Now you
can exit the shell by hitting Ctrl C twice to get out. Or you
can also just you can also Trash Can your terminal if you get
confused. This console works with any network we can even do
yarn, hard hat console dash dash network hard hat. And we'll get
dropped into a hard hat networ
k. Now this is not going to be the
same node that's running here, this is going to be one that
only runs for the duration of this command. So whenever we
cancel this command, this hard hat network gets cancelled. And
close out that too. We can also do yarn, hard hat, console, dash
dash network Rinkeby Rigby, or polygon or test net or main net
or whatever we want. And we can do things like ethers dot
provider that can do things like await ethers dot provider dot
get block number, see the block nu
mber of Rigby. We can also
deploy contracts, we can update contracts, we can do anything
that we want, you can do anything in these consoles, and
they're great ways to quickly test and tinker in interact with contracts. Now, there's a
couple other tasks that are really helpful. You'll see
before I went ahead and just deleted artifacts and deleted
the cache manually. Well, to do that yourself, you can also just
run yarn Hardhead, clean. And that'll delete the artifacts
folder and clear out your c
ache. We already know what compiled
does. But one of the biggest things that Hardhead is
fantastic for especially is running tests. Now we haven't
run tests yet so far. However, running tests is absolutely
critical to your smart contract development journey. And we're
going to spend a lot of time in the future writing really good
tests. The reason that writing tests are so important is
because we want to make sure our code does exactly what we want
it to do, especially in the defy, and the decen
tralized,
smart contract world, all of our code is going to be open source
for anybody to interact with, and potentially exploit. There's
sites like req dot news, which go through a ton of previous
hacks and how they actually got hacked and what happened in the
smart contract to enable these hacks to occur. So testing, so
writing really strong tests is always going to be our first
line of defense. And we have this sample test.js, that comes
default with the basic package of hardhat. But as you p
robably
already know, we're going to rename this and change it. So
we're going to rename this to test deploy dot j s. And we're
going to delete everything in here and start from scratch. We
want to be able to test all of our solidity code locally, so
that we know exactly what it's doing. And we can have a
programmatic way to make sure that our code does what we want
it to do. So let's write a basic test for our simple storage
contract. So that we can be sure that it's doing exactly what we
want
it to be doing. Hard Hat testing works with the Mocha
framework, which is a JavaScript based framework for running our
tests, you actually can write tests directly in solidity, if
you'd like to, there's a bit of back and forth on whether
testing with pure solidity is better or testing with a modern
programming language. The argument goes that testing with
a modern programming language, you have more flexibility to do
more stuff to interact and test your smart contracts. But the
argument for test
ing with us with solidity is that we want to
be as close to the code as possible. At the time of
recording, most projects do the vast majority of their testing
in a modern programming language like JavaScript. So that's what
we're going to be using here. So to get started with our mocha
tests, we do, we're going to write a describe function.
Describe is a keyword that hardheaded mocha will recognize,
and it takes two parameters, it takes a string, which we're
going to just write simple storage f
or now. And then also
takes a function, we could make function, test func and then
write some stuff in here, and then pass it to our describe
here. But the common convention is going to be to do is to make
it as an anonymous function, which we can create by typing
function, putting an empty parameter here, and then some
brackets like that. So our describe function takes a name,
a string, and a function. Another way that you'll often
see functions in describe is using that anonymous function
synt
ax. So you might see just these parentheses, an arrow, and
then some brackets, the two of these are going to be basically
the same, there are some differences. And this second one
is actually best practice, but just know that you might see
this arrow syntax in other tests as well. We have described a
sample storage and then our function here, which is going to
have all of our tests in it. Inside each one of our describe
blocks. We're gonna have something called a before each,
and a bunch of It's
Our before each function is going to tell
us what to do before each of our hits. So we're going to have a
ton of it and then we're going to have a before each. All of
our IDs are going to be where we actually write the code for
running our tests. And before each is going to be some code
that tells us what to do before each one of these hits, we can
also actually have describes, inside of describes, which again
have more before each and more before it's having these nested
describes can be reall
y helpful for separating and modularizing
our tests. But for this one, we're just going to have a setup
that looks like this. And for this demo, we're only going to
have one it. So in order to test our smart contracts, before we
actually run our tests, we're probably going to need to deploy
the smart contracts first. So inside of our before each, we're
going to pass the our before each a function, that's going to
tell our testing framework what to do before each test. So we're
going to pass it a
n async function like this. And in here,
we want to deploy our simple storage contract. So to do that,
we're going to need to get the ethers framework and do exactly
what we did in our deploy script. So in here, we're at the
top we're gonna say const. Ethers, equals require art have
an import ethers from hard hat. Then in our before each
function, we'll say await ethers dot get contract factory of
simple storage. And we'll assign this to a const. Simple Storage
factory. And then we'll run away,
simple storage factory dot
deploy. Cool. And let's also assign this to a variable cost
simple. Now, since right now, our simple storage and simple
storage factory are scoped just to inside the before each, we
actually need to stick these variables outside of the before
each, so all of our events can interact with them. So instead
of having simple storage, factory, and simple storage, be
constant variables, we're going to define them outside of them
for each with the let keyword. And we're gonna
say let simple
storage factory and we're going to initialize it to nothing. And
then we'll say led simple storage. Now, if you have a
whole bunch of let's just initializing another way, you
can write them in JavaScript, just let simple storage factory
comma, simple storage. And that works exactly the same. And then
we can get rid of this const keyword. Because it's not a
constant since we are assigning it. And now we have simple
storage, factory and simple storage that we can use inside
of our i
nit function. Now we have a before each section. So
before each one of our tests, we're going to deploy our simple
storage contract. So we have a brand new contract to interact
with for each one of our tests. Now, inside of the ID, this is
where we're going to say what we want this specific test to do,
and then describe the code that's going to actually do
that. So we're going to say it should start with a favorite
number of zero. So this is saying what this test should do.
And then we're going
to add our async function to actually do
that. So we'll say async function. And in here, this is
where we'll actually write the code to make sure that our
contract does exactly this. We're say const. Current Value,
equals await, simple storage.re retrieve. And now in this test,
we want to say okay, now check to see that this current value
is indeed zero. So how do we do that? Well, we can say const,
expected value is going to equal zero. And what we can do is we
can do either we can use either t
he assert keyword, or the
expect keyword, which we're going to import both of these
from a package called Chai. We actually installed Chai
automatically when we downloaded the basic parameters when we
downloaded the basic packages for hardhats. So at the top,
we're gonna say const. Expect and assert equals require Chai, I'm a big
fan of using assert as much as possible, because I think the
syntax makes a little bit more sense. But there will be
scenarios where we need to use expect instead. Now,
assert has
a ton of functions that are built in that help us make sure
this is what we expect it to be. So I can do assert dot equal
current value.to string, because remember, this is actually going
to be a big number, comma, expected value. So I'm saying
I'm asserting this retrieve to return zero, which is going to
be our expected value. Now to actually run this, we're going
to run yarn, art hat test. And we see we get an output that
looks like this should start with favorite number of zero,
a
nd it's indeed passing. You'll notice that if I were to change
this to one, and this wasn't correct, it would break and it
would say art Pass or not passing assertion error expected
zero to equal one. It expected zero to equal one, which is not
what we want. We want zero to equal zero. So let's run this
again. Tada should start with favorite number zero, and it's
passing. Alright, fantastic. So that's how we wrote one of our
tests, let's write one more test just to make sure that things
are good
. So let's say it should update when we call store,
because when we call the store function, we want our favorite
number to update. And we'll make this an async function as well.
And let's add our stuff in here. So we'll say const. Expected
Value equals seven, we're expecting that when we call
store it updates to seven. Now we can say const. Transaction
response equals await simple storage dot store. And we can
even just pass it the expected value here. And then we'll do a
weight transaction res
ponse that weight one. Now let's get the
current value. So we'll say const. Current Value equals
weight, simple storage, retrieve. And now we're going to
assert dot equal current value.to string, comma expected
value. And now we can run all these tests by running yarn
Hardhead test. And you'll see we ran both of these tests. And now
if I have 10,000 tests, and I'm only finagling with one test, I
can actually just run one test by running yarn, art app, test,
dash dash grep. And I can search for a
ny keywords in any of the
text here. So I'm going to grep for the store function. Because
the store keyword isn't in this tax for this, it, it's only in
the text for this it. So if I do grep store, it should only run
our second test, which does indeed, one other way we can run
only specific tests is with the only keyword. So we can type it
dot only like that. And then we can run yarn, art hat test. And
it should only run, this should update when we call store. And
it does indeed, then we'll go a
head and delete this save, run
again, and it should run all too. Fantastic. Now the other
way you'll see these tests written is with instead of
assert, it'll use the expect keyword. So you'll see something
like expect current value.to string.to dot equal
expected value, the two of these lines do exactly the same thing.
And it's sort of up to you on which one you want to use. And
that's all we're going to do for our testing. Now. This is
fantastic, great job. Now that we have some tests, we can
a
ctually start testing to see how much gas each one of our
functions actually costs. One of the most popular extensions for
hard hat is the hard hat gas reporter. This is an extension
that gets attached to all of our tests, and automatically gives
us an output that looks like this, that tells us
approximately how much gas each one of our functions cost. We
scroll down in here, we can read the instructions on how to
actually install this npm install Hardhead gas reporter
which we're going to use w
ith yarn. So we're gonna say yarn,
add hard hat, gas reporter, dash dash Dev. And now that that package is
installed, we can go over to our config and add some parameters
in here so that we can work with this gas pit. But our neath our
ether scan section, we're going to add a new section called gas
reporter. To have it run, whenever we run our tests we're
going to do enabled is going to be true. And then up at the top,
we can add it by adding require hardhat gas reporter. Now that
we have it in
here, we can do yarn hardhat test, and after we
run our tests, it'll automatically run this gas
reporter. So we see our tests go ahead and run. And then we get
this output that looks like this that tells us how approximately
how much our contracts and methods cost. So our store
function looks like It costs approximately this much gas and
our simple storage costs approximately this much gas.
This is incredibly helpful for figuring out how to optimize our
gas as best as possible. Now I usually lik
e to take it a step
further though. Having the gas output it like that is nice, but
we can make it even better. I like to output it to a file by
doing output file. Yes, report dot txt and then my dot get
ignore like to add it in here, but doing gas report dot txt
sentence it's not really important for the gas report to
get pushed up to GitHub. Do no colors is true. The reason we
add this is because when we output to a file, the colors can
get messed up basically. And then the biggest addition we
could do is we can add a currency in here. So that we can
get the cost of each function in USD for a blockchain like
Aetherium. Now in order to get a currency here, we actually need
to get an API key from corn market cap, just like we did
with ether scan, you can go to coin market cap, corn market cap
API, get your API key now, and we'll go ahead and sign up.
Choose a basic plan. We'll agree and create my account, we'll get
an email verification. And we'll go ahead and verify. Now in the
coin m
arket cap dashboard, we can copy our key. And yep, you
guessed it exactly what we're going to do with this key, we're
going to drop it into our dot env file, or say coin market
cap, API key equals and then paste it in there like that. Now
that we have our corn market cap API key in here, we can go back
to our header dot config and add it in this corn market cap
parameter. We're gonna do the exact same way we did above,
we'll do const, coin, market cap, API key equals process dot
EMV, that coin m
arket cap API key, and then we'll take this
sticking in here. So what this is going to do is actually going
to make an API call to corn market cap, whenever we run our
gas reporter. This is why sometimes you'll see me comment
this out and uncomment it because I don't always want it
to make this API calls. But now what we can do now that it's
enabled, we have an output file, we can see the currency and we
have our API key, all we can do is run yarn hardhat test. And
after all our tests pass, we'r
e going to see a gas report.tx T
that we can go ahead and read from which has that gas report.
And now it actually has the USD price of each one of these
transactions, it looks like at current prices, with Aetherium
being $3,000 per eath, and a gas price of 43 Gwei, the store
function would cost $6. And the simple storage function would
cost $64. The current the Hardhead gas reporter actually
comes with some different options, though, if you're going
to be deploying to different network, for exa
mple, with
binance, Polygon, Avalanche or hecho. For example, let's say we
wanted to deploy the polygon, let's see how much deploying the
polygon would cost well in our WMV UHD ad token, Matic And now we'd rerun this
test. And if we look at our gas report that takes T will now see
the gas price of polygon right now is around 37 Gray per gas.
And the cost of Matic is 147 automatic and USD. Now we can
see the cost of calling the simple storage method is going
to be $0.00. Now this of course is rou
nded down, but it's going
to be really, really cheap to call store versus deploying the
contract is going to cost three cents, I make it a habit to
select false for my gas reporter whenever I don't want to
actually work with the gas here. Awesome. Now sometimes when
we're working with our code, if we don't have these environment
variables specified Hardhead might get a little bit upset
with us. So oftentimes, I'll add some code in here. So that these
variables are always populated, because we di
dn't specify our
rink prpc URL, ring P RPC URL is going to be undefined. And that
might throw some errors blow. So oftentimes, what we'll do is
I'll add an or parameter here, these double pipes mean or, and
in JavaScript, if we say some variable equals something or
something else, what is really happening is we're gonna say,
okay, rink, the RPC URL is going to be equal to process dot E and
V dot rink, the RPC URL. But if this rink, the RPC URL doesn't
exist, it's going to be whatever else is ove
r here. And I might
write something like HTTPS eath Rinkeby. Example, or something
like this, just so that I don't make hard hat mad if I don't use
rank B. And we can do something like that for all these. So
you'll see this syntax, oftentimes in a lot of code
setups. Now the last thing that I'm going to show you before
going into the TypeScript edition of This is test
coverage. And as we progressed to this course, I'm going to
show you more and more tools that you can use to make sure
that our s
imple storage contract is safe and secure. And we take
all the steps we can to prevent any hacks from happening if we
deploy in real life. One of those tools is a tool called
solidity coverage. And this is also a hardhat plugin that we
can use for our code. solidity coverage is a project that goes
through all of our tests and sees exactly how many lines of
code in our sample store dot Sol are actually covered. And this
can be a Good tip off. If we don't cover some line of code,
solidity coverage
will say, Hey, you don't have any tests for
this line, maybe you should write some tests for it. We can
add solidity coverage the same way we've been adding all of our
packages. npm install dash, just save Dev, or since we're using
yarn, yarn, add dash dash Dev, solidity coverage. And we can
then add this to our config, the same way we've been adding
everything to our config, go to our config, and we'll write
require solidity coverage. And there's some configuration
pieces we can add down here
below for this, but we're just
going to use the default now we can do is run yarn, RT hat
coverage. And this is going to go through our tests and print
out a file that looks like this, we'll also get a file called
coverage dot JSON, which is basically this chart broken down
a little bit more often put my coverage dot JSON in my dot get
ignore. And I know we haven't actually seen dot Git ignore, do
what it's supposed to do. But we will soon we can see here that
about 50% of the code 50% of our st
atements in simple storage dot
soul are covered. About two thirds of our functions are and
50% of the lines, It'll even give us exactly what lines
aren't tested right now, which we can see exactly 31 and 32 of
simple swords, outsole aren't covered, which makes a lot of
sense, because 31 to 32 is this Add Person function, which we
didn't call and we didn't add to our tests. If you want to take
this time to pause and try to make this solidity coverage be
100% across the board, by writing some more
tests, I
highly recommend you do so it'll be a great learning exercise.
We'll also add the coverage folder. So covered adjacent and
the coverage folder, which again, I'll explain what the dot
get ignore folder does a little bit later. Now the last thing
that we didn't talk about and here was what is this nomic labs
hard hat waffle. We talked about Daddy and V heart and ether scan
tasks gas reports. So today, what is this? Well, we can
actually Google search this and find out exactly what this i
s
Hardhead waffle is actually a plugin to work with the waffle
testing framework. waffle is one of these frameworks that allow
us to do some really advanced testing, we're going to be
working with some syntax that looks really similar to this
really soon. And we'll be showing you more and more of
this waffle tool as we continue. Alright, the
next part of this section, I'm actually going to go over the
TypeScript edition of this. But for all intents and purposes,
you've successfully created your
first Hardhead project, you've
done a ton of amazing things. In this lesson, let's do a quick
refresher of what we've learned so far, we learned how to spin
up our own hard hat projects. And now we can run yarn, hard
hat and see a list of the tasks and different things that we can
do with hard hat. We learned that hard hat looks for this
hard hat.config.js. And this is sort of the entry point for any
task that we run that starts with hard hat, we learned we can
add our contracts to this contract
s folder. And then we
compile it by running yarn, art half compile, we learned that
all the compliation goes into the artifacts and then the cache
as well. And if we want to clean reset, we can either delete
these two files or just run yarn, hard hat clean. So we
learned that we can use scripts or tasks to actually deploy,
interact and do things with our smart contracts. We also learned
that I'm going to be using scripts for the rest of this
course. But if you want, you could absolutely use task
s as
well. I've asked this question a million times what's the
difference? Nobody really seems to know what the main difference
is. But I think the main difference is that tasks are for
plugins. And scripts are for your local development Mart,
that is mind limiter, we learned that we can import a whole bunch
of things, including tasks from hard hat in our scripts, and we
can work with our async functions to grab our contracts
and deploy them, we actually then can programmatically verify
them usi
ng hard hat and using hard hat plugins. And then
additionally, we can interact with our contracts very similar
to how we did it with ethers. We wrote a wonderful verification
script. And we also wrote our own task, we wrote our first
test for this whole space. And we showed what our tests are
going to look like moving forward. And we talked a little
bit about their importance. And I really should stress that
writing good tests is going to be the difference between a
really professional environme
nt and kind of a side project.
Whenever I audit smart contracts, or whenever I'm given
a project for someone to tell me to take a look at. The first
thing I look at is the readme, of course. And the second thing
I look at is the tests. And if tests aren't good, I usually
tell them, hey, you need to go back to the drawing board, and
you need to level up your tests. So tests are really really
important, especially for this space, we learned about a couple
of more environment variables we can use.
We learned about a
couple of tools to see how good our tests are one of them being
coverage. We also learn about a gas reporter to see how much
it's going to cost us when we actually deploy to a real
network. We learned a ton about the hard hat config, and how
there are multiple networks that we can add add to our hard
heads, we can make our project our EVM code, work with any
network out there, we started working with dev dependencies
instead of regular dependencies. Now, READMEs are something
that
I'm not really going to go over too deeply in here. But READMEs
are sort of like the welcome page of your GitHub repository,
and really should give you an understanding of what your code
does. Being a part of the web through space. And being a part
of the blockchain ecosystem is really more than just you coding
your stuff by yourself. You want other people to interact and
engage with your code and engage with your projects. I haven't
showed you how to use GitHub yet. But don't worry, we're
going to but if you look at my heart had simple storage readme.
If you scroll down, usually, you really want to have a Getting
Started section where you define how to set up all the code and
how to set everything up a Quickstart section, and maybe a
usage section and some testing section, which teaches people
how to actually use and interact with your code. Since we're just
learning more of the code part and not so much the readme part.
For now, we're not going to go over how to make a fantastic
readme. However, I will leave a link in the GitHub repository
associated with this course, link to this best readme
template. It really is a fantastic readme template that
you can copy to any of your projects to make them look
really good and give them a really good setup, so that other
developers can come to your project, and learn and
participate with what you're coding. But all right, you have
learned an absolute ton, you should be incredibly proud of
yourself, and incredibly excited that yo
u've made it this far.
Now I'm going to jump into the TypeScript section here. So for
those of you who are coding along with TypeScript, feel free
to follow along. For those of you who are not, you just
finished the Basic section on hard hit, but stick around the
next to heart out sections are going to be the ones that really
fine grain and hone your skills, and give you all the
fundamentals for working with these frameworks. So be sure to
follow along with the next two sections, we've got a ton
more
fantastic content for you, we are just beginning to get deeper
into the smart contract ecosystem. So take that lap, get
that coffee, and I'll see you soon. Alright, also now let's do
this with TypeScript. So I am going to go ahead and just start
this from our JavaScript section. However, if in the
future, you want to start a new Hardhead project, you can
actually start a new project with yarn, hard hat. And then do
great and advanced sample project that uses TypeScript,
you'll add a ton of
plugins. And you'll wait a while for
everything to get uploaded. And you wait, and you wait a little
bit for everything to get downloaded. We're not going to
do that though, because I'm going to show you how to convert
this to JavaScript Anyways, if in future hearted sections, as
we're coding along with JavaScript, if you want to code
along with TypeScript, you absolutely 100% can. But let's
go ahead and show you what the main differences are. Now that
advanced TypeScript thing is going to add
a whole bunch of
packages that you may or may not want, I will talk about some of
them in our next lesson. But there are going to be some that
you absolutely do need. Those are going to be at type chain
slash ethers, dash v five, at type chain, slash hard hat, at
TypeScript, at types, slash Chai, at types, slash node, at
types slash moko, TS node, type chain and TypeScript. And I have
a link in the GitHub repo associated with this course,
I've got this yarn ad that you can just copy paste, if yo
u want
to just copy paste that into your project to run it. Oops,
and I should have added those as dev dependencies. So we're going
to actually just make them dev dependencies real quick, just by
deleting these two lines, and adding a comma here. Awesome,
that looks much better. And then of course, what we're going to
do is we're going to convert all of our JavaScript to TypeScript.
So anywhere where we have J S, we're going to put Ts,
obviously, if you're coding this from scratch with TypeScrip
t,
you would do the.ts From the get go. This includes our hard hat
dot config, that's also going to be TypeScript now. And
additionally, we're going to add a TS config dot JSON. This is
going to be our TypeScript configuration. Typically, for a
setup, we're gonna go with something like this. And you can
copy this from the GitHub repo associated with this course.
It's basically telling TypeScript, what versions of
Typescript and what files to include for working with
TypeScript. Now let's go ahea
d and start with our deploy dot
TypeScript. For usual, instead of using require, we're gonna go
ahead and use import, we're gonna do the exact same thing.
We're gonna have import ethers run network, from hard hat. And
then in our verify function, we're going to add that we're
gonna add the types for these arguments. So contract address,
is going to be a string. And args is going to be an array of
arguments. So we're gonna say it's gonna be any array, because
it could be string, it could be numbe
rs, it could be balloons,
it could be anything. We're also going to say for IE, it could be
any, even though this is technically an error type, we're
just going to put any for simplicity. For now. All of our
TypeScript scripts are included in our TS config, or any
TypeScript files are manually added here, which we have Our
entire scripts folder here, which is good. So now we need to
add ethers in here. Well, if we look at our hardhead.config.ts,
we're using require here still, and we need to swa
p this out for
import for Donnie V, you can use dot env slash config for it to
grab your dot env file. Now that we've imported everything, go
back to our deploy.ts, we can see that that linting has gone
away. If you want to be even more explicit, we can go ahead
and add import at nomic labs slash hard hat ethers like so.
reason we don't need to import it here is because these two
packages also work with Hardhead ethers, so they automatically
import it. But if you want to be super explicit, you c
an go ahead
and add it like so. Now, we're almost good to go. But remember,
our Harnett dot config is also importing our tasks. So we're
going to need to update our tasks or block number to be
TypeScript fide. So of course, instead of const require, we're
going to import task from our that slash config. And we're
going to be sure to export our task from lock number as the
default. So we're going to do export default task like so. And
now we should be good to run our scripts. So we can just do ya
rn,
art hat, run scripts, employee.ts. We can do network
hard hat if we choose. And awesome. Now, it's when we get
to the testing, that things get a little bit different here. So
let's go ahead and change this required to import. Just to make
it happy there. Let's try to run yarn, art head test, we get a
whole bunch of errors, and in VS code will actually go ahead and
get these errors right from the linter. One of the trickiest
things that you run into as a developer in this space is
calling fun
ctions on contracts where those functions don't
exist, or vice versa. We're not calling functions on contracts
that do exist. Right now the typing for our contracts is just
type contract, which isn't super helpful, because type contract
doesn't necessarily have all the functions that we want it to
have. We want our contracts to be of type contract, but we want
them to be of the type of our contract, because if they're the
type of our contract, they can have all the functions that we
want them to
have. So to give our contracts the correct typing
here, we actually can use this tool called type chain, which
gives our contracts correct typing type chain has a hardened
plugin, which allows us to use type chain and TypeScript
natively together, type chain slash hard hat was one of these
things that we already installed. And to add it to our
hard hat, we got to just go to the hard hat config, and add it
in import at type chain slash arhat. Now once we import that
in to our config, if we run y
arn hardhat, we now get a new task
here called type chain, you read the description, it says
generate type chain typings for compiled contracts, this will
enable all of our contracts to have their own typing. So we can
have a simple storage variable of type simple storage contract,
which is much better, because we're always going to know
exactly what we can do with each contract. To create this, we run
yarn, arhat type chain. And this is
going to create a new folder called type chain slash types
.
With types for all of our contracts, you can even go into
our simple storage sub Ts, which is going to have all the
different functions and everything to do with our simple
storage contract automatically coded into Typescript and
JavaScript for us, which is incredibly helpful. And again,
no, I haven't shown you what this is yet. But in our dot Git
ignore, we usually want to add type chain and type chain dash
types into our dot Git ignore so we don't push them up to GitHub.
Now back in our test
, we're going to add the exact types of
these different objects here. So we're going to import them from
that folder that we just created. So we'll do import
simple storage, comma, simple storage underscore underscore
factory from dot dot slash types, ain slash types, the
simple storage factory is going to be simple storage factory and
then civil storage of course, is going to be simple storage. So
now when do let simple storage factory which is going to be of
type, simple storage, underscore un
derscore factory, and then
simple storage, which is going to be of type simple storage
contract. And if we command clicked into simple storage,
once again, we can see all the contract functions that we know
and love, are here. In addition, we have all the functions of the
actual contract itself. Once we do that, we're pretty much good
to go we just need to have a couple of new things here. Get
contract factory returns a type ethers dot contract factory. So
what we just need to do is we need to w
rap this in a simple
storage factory type. So We'll just do a little wrap like this.
And we'll say, as simple storage, I'm just going to sort
of factory. And that's good to go. Now that we've added all
this, we can run yarn, Hardhead test. And boom, our test run is
normal, but with TypeScript and with this additional typing that
makes our lives substantially substantially better. And that's
going to be all you need to know for TypeScript, reusable, all of
the branches have an optional TypeScript
branch that you can
use to reference to work with TypeScript. We've learned really just the
basics of all the different things we can do with hardhat.
And these next few lessons Hardhead fun meme and hard hat
smart contract lottery are really going to be the basics
for all the fundamentals of all the tools that we're going to
learn in hardhat. Lesson Eight is going to be our introduction
to full stack and working with front end and building full
stack applications. Getting all the way through t
his course will
give you all the tools to start your web three journey. But if
you're looking to just learn just the basics, make sure you
absolutely get all the way to Lesson Nine. And if you get all
the way through lesson 18, you are going to know all of the
cutting edge tools for this space. And you're going to have
the knowledge to become easily one of the best developers in
the space. So hope you make it all the way through to the end.
Now one of the most important parts of this section of
this
lesson is going to be pushing our code up to GitHub, and then
sending a tweet celebrating that we pushed our first smart
contract our first web three GitHub repository to GitHub. So
before moving on to the next lesson, be absolutely sure to
get to the end of this and push this code up to GitHub. And then
optionally, if you want to celebrate by sending a tweet,
but be absolutely sure to get to the GitHub section, because as
I've said, many times the web three space is this incredibly
collabo
rative community and working with GitHub or GitLab,
or any other version control tool is going to be essential
for your success in the space. So be sure to get to that part.
All right. Now, welcome back to the hard hat Fund Me section of
our course. This is the section where we're actually going to
upload our first code repository to GitHub if you've never done
this before, this is going to be the section where we're going to
learn even more about hardhat using a familiar contract base
we've alr
eady worked with, which is the fund me contract. And
again, if you're using the GitHub repo associated with this
course, you can scroll down to the Hardhead Funmi. And all the
code is located in our repo. If you'd like to do a quickstart,
you can go ahead and get clone it CVU into it, and then run
yarn, and then just run yarn hardhat deploy this, I'm going
to briefly show you what that looks like. So in your in your
VS code, you can do git clone, grab the package, cd into it,
and then type code
period. To open it up in a new VS code.
Once you're in your folder, you can go ahead and run yarn. To
install all the dependencies for working with this project, you
plan on working with the test net or working with ether scan
or coin market cap, feel free to fill out your Dotty and V with a
private key RPC URL, corn market cap key and ether scan key. And
then you can just follow along with the readme to use this repo
to run yarn, hard hat deploy. And it'll show you deploying
some contracts and
some mocks, etc. So let's get to building
this ourselves though. Now we're gonna make a new directory for
this project, it's gonna be the same setup we've seen before. MK
dir, hard hat. And me, FCC, we're gonna cd into heart at
Funmi, FCC, and then type code period. And if code period
doesn't work for you, you can absolutely open this up by
hitting File, Open Folder, like we showed you before. Now, we're
in a brand new folder here. And we're gonna go ahead and add
hard hat here. And we're gonna
run yarn, add dash dash Dev,
hard hat. Now that we have hard hat, in our package, JSON, and
in our node modules, we can go ahead and run yarn, start app.
And this will say, What do you want to do? I'm going to choose
the advanced sample project here, just to show you what's
going on. And we're going to set this up in a way that I think
works best. So we'll go ahead and do the advanced sample
project. Yes, we're going to have that as the root. Yes, we
want to add a Git ignore. And there are a lot
of sample
project dependencies that it wants us to add. We're gonna go
ahead and hit yes. But we're going to end up not using all of
these and I'll show you which ones we're not going to use and
why. But for now, let's go ahead and hit yes. All right, awesome. And now we
have an advanced project in here. Let me walk you through
the additional things that are in here. So we have a
traditional contract node modules, which is going to be
the same scripts is going to be the same test is going to be
the
same. But this comes with a dot E and V dot example already
packed in for us. It also comes with.es lint files.es lint, RC
dot j s.es. Lint, ignore es Lint is known as a JavaScript linter,
which helps you find and automatically fix problems in
your code for the JavaScript that I work with. I'm not a big
fan of ES lint, so I typically don't use it. So I'm going to go
ahead and delete the two of these. If you want to keep them
in you absolutely can dot Git ignore. We're going to finally
under
stand what this file does in this lesson that NPM ignore
helps Ignore files if you want to push your project up to be an
NPM package, which we're not going to do. So if you want to
delete this, you can as well pretty or ignore it and pretty
DRC. We already know what these do small hint. And so hint
ignore, which we're going to talk about it in a minute, or
Hardhead config, which just comes already with a ropsten
network, a gas reporter and ether scan package dot JSON with
all the additional pack
ages, the readme is a little bit more
robust. And then of course, our yarn dot lock. So this advanced
project looks pretty similar to what we're going to be working
with anyways. Now I do want to talk about this soul hint,
though. So what is solvent solvent is known as a solidity
linter that we can use to lint our code linting is the process
of running a program that will analyze code for potential
errors. It also does a little bit of formatting, oftentimes,
es Lint is a way to lint for JavaScri
pt code. So hint is a
way to lint for solidity code, we use prettier to format our
code, and we can use soul hint to lint, our code, they are
often used a little bit interchangeably, even though
that's not exactly correct, as they are a little bit different.
We can run this linter on our code by running yarn, sole hint,
and then type the name of the files that we want to lint. So
we do contracts, slash and then you can just do start out so
everything looks okay, nothing will happen. Well, let's
say we
have a variable that we don't explicitly say the visibility of
it is. This isn't the best practice because ideally, we
always say exactly what the visibility of some variable is.
This obviously gets defaulted. But it's usually better to be
more explicit. So now if we run yarn, so hint, contracts start
up so it'll give us a warning, saying we should explicitly Mark
visibility of state, this linter is a good way to check for some
best practices for running our code. So we're definitely goin
g
to keep a small hint around. Now that we've got a repo here,
let's add a couple of our common setup pieces here. So in
prettier.rc, we're going to swap this out with what we've been
using so far. Tab of the form us tab is false, semi false, single
quote, also false. We're going to update our prettier dot
ignore node modules, package dot JSON image artifacts, cache
coverage ID v dot star, readme, and coverage and anything else
you want to add in here. And we're going to scroll up to our
contrac
ts folder. And we're going to swap this greeter dot
soul out with our fund me dot soul. Now let's go ahead and add
our contracts in here. If you're following along with the repo,
you go to the contracts folder, there's actually another folder
in here and the contracts look a little bit different. So if you
have those contracts, steal from remix, let's actually grab them
from remix because we're going to make a couple of changes to
them. If you don't have remix up anymore, which you probably
shou
ldn't, because you should be taking breaks, you can jump back
over to Lesson four remix Funmi jump into the repo here and grab
the contracts from inside here. Just go to the fun v dash Free
Code Camp tutorial and grab the code from there. So we're gonna
grab just fun me and price converter dot soul. So go ahead
and delete that old file, create a new one and call it fund me
dot song. Paste it in there. And then we're going to create the
price converter. That's all. Now we have both our Funmi and
our
price converter contracts in here. Now one of the first
things that we want to do one of the first things that we did
last time was we ran yarn compile to make sure that our
code is actually working the way we want to. And before we
actually hit Compile, one of the things that we're going to need
to do is come to our Hardhead dot config, we're going to make
sure we're on the correct solidity version. So we're going
to do zero point 8.8 here. And let's go ahead and try to
compile. So we can r
un yarn Hardhead
compile. And you'll see we actually get an error here.
Library at chain link slash contracts imported from
contracts slash fund me dot soul is not installed, try installing
it using npm. In remix, we went ahead and just imported at
chainlink slash contracts, right from our NPM and or GitHub. But
in our local code, we have to tell Hardhead specifically,
where to get this from, we want to download this specifically
from the NPM package manager at chainlink slash contracts, we
can
download it simply by running yarn, add dash dash dev
at chainlink slash contracts. Now that we've downloaded it
into our file, we'll be able to see it in Node modules here.
Hardhead is now smart enough to know that at chain link slash
contracts is going to point to that node module that we have.
So we can now run yarn Hardhead compile boom, now we can see
compiled three solidity files successfully. So now we have our
contracts in here and our code is compiling successfully. We're
probably going
Want to deploy our code? Now in our last
section, I know we use the scripts module. And we made our
own manual deploy script. However, something that you'd
notice, the more that you work with just raw ethers, or even
just hard hat is that keeping track of all our deployments can
get a little bit tricky. If you just use a deploy script, it's
not saving our deployments to any file. Additionally, having
everything in the deploy script for deploying can make the tests
and the deploy scripts, maybe
not work exactly hand in hand.
And there are a couple of other things that might be a little
bit tricky to work on, we're actually going to work with a
package that makes everything I just mentioned, and a couple
other things way easier. And this package that I'm talking
about is going to be the hard hat deploy package. There's a
link to this package in the GitHub repository associated
with this course. It's a hardhat plugin for replicable
deployments and easy testing. And if we scroll down to
i
nstallation, we can see we install it basically the normal
way. They're using npm. And we're gonna go ahead and use
yarn. So for us to add it, we'll do yarn, add hardhat dash
deploy. And then of course, we're gonna do dash dash Dev.
Once done deploying this require statement to our hard
hat.config.js. Once again, basically the config is our
entry point. This is where we're gonna get started. And we can go
ahead and delete our deploy.js script. Now if we run yarn
hardhat, you see that we have a b
unch of new tasks in here, with
one of them being this deploy task, this deploy task is going
to be the main task that we use to deploy our contracts. Instead
of writing our deploy scripts in the Scripts folder, we're
actually going to create a new folder, we can create a new
folder by just doing MK dir deploy. Or you can always right
click and hit New Folder. This deploy folder is going to be
where a lot of hard hat Deploy Module looks to deploy code. And
it's going to be where we are writing o
ur scripts. To write
our scripts, we usually need to add one more thing in here.
Since we're going to be using ethers JS in all of our scripts,
we want to add Hardhead deploy ethers to our package here. Now,
instead of just doing yarn, add dash dev hard at deploy ethers,
we're going to do something a little bit weird. We're going to
do yarn add or npm install dash dash Dev, and we're going to
install it like this. So let me just copy this. And you can just
copy that from the repo. And we'll do y
arn, add dash dash
Dev, and paste that in here. What we're doing is we're taking
at nomic labs, hard hat ethers, which we've used before, and
we're overriding it with hard hat deploy ethers. Remember how
in our last project, we used hard hat ethers. So that hard
hat could override ethers to use hard hat deploy. We use hard hat
deploy ethers, so that hard hat deploy can override Hardhead,
which overrides ethers, which is kind of funny. When you say like
that, this will enable ethers to keep track
of and remember all
the different deployments that we actually make in our
contract. So if we look at our package, JSON, and now we can
see our nomic labs dashboard had ethers. Now the version of it is
going to be MPM. Hardhead deploy ethers. This is our package dot
JSON, basically saying the hard hat ethers package is now
overwritten by the hard hat deploy ethers package, which is
what we want. Alright, great. So now that we have that setup, we
can start writing our deploy scripts, the way tha
t Hart had
to play works is all the scripts that get added to our deploy
folder will get run when we run yarn, hard hat deploy. So a good
practice is usually to number them so that they run in the
order that you want them to run in. So since we only have one
contract that we want to deploy the Funmi contract, we're going
to do 01 Deploy Funmi Jas, and in this script, this is going to
be where we define how to deploy the fundament contract. Alright,
so we're in our deploy Funmi scripts. Now tradi
tionally, what
did we do, we did imports, we did the main function. And then
we did calling of main function, that Hardhead deploy is a little
bit different, we're still going to import our libraries and
packages, but we're not going to have main function. And we're
also not going to call the main function when we run Hardhead
deploy Harnett deploy is actually going to call a
function that we specify. In this script here. What we're
going to do is we're gonna create a function, we'll call it
dep
loy funk. We're going to export this
deploy function as the default function for Hardhead deploy to
look for, so we could say, module that exports dot default
equals deploy funk. To test it out, we can go ahead and do
console dot log, hi. And then in our terminal, run yarn, hard
hat, deploy. Oops, get rid of the parentheses here. Sorry. Run
it again. And we can see it went and ran our deploy func here.
Now if this syntax is easier for you to understand, go ahead and
use this syntax and we're goi
ng to be passing the heart at
runtime environment as a parameter to this function.
However, if we go to the heart hat deploy documentation, and we
scroll down to an example script, the syntax looks a
little bit different. And let me just explain what's going on
here and how we're going to be writing ours. So instead of kind
of defining everything like this, and defining the function
name, similar to what we were doing before, we're actually
going to using a nameless, a synchronous function, we'r
e
going to make it an anonymous function, similar to what we've
seen before. So instead, we're going to say async parameters
like this, I'm going to pass our parameters our heart at runtime
environment in here. And it's going to be an arrow function.
And then we're going to wrap this whole thing in module dot
exports. So we're gonna say, module, dot exports, equals this
async function like this. This syntax here is nearly identical
to what's up here, we just don't have a name for our async
funct
ion. So this is how we're going to set it up instead. But
if this syntax is a little bit confusing for you feel free to
use this above as the two of these are going to be the same.
Now the next thing that most of the documentation does is it
pulls out the variables and functions out of the HRV that
we're going to use. HRV is the heart hat runtime environment.
Whenever we run a deploy script, heart hat deploy automatically
calls this function and just passes the hard hat object into
it similar to
in back in hard hat simple storage. In our
deploy script. We had ethers run in network come from hard hat,
instead of coming from hard hat. We're coming from HRV, which is
basically the same thing as hard hat. For our script, we're only
going to use two variables from a jury when you use const. Get
named accounts. And deployments. This syntax might look a little
bit weird for you. But it's just a way to pull these exact
variables out of a tree. It's kind of the same thing as just
doing a tree d
ot get named accounts and HRA dot
deployments. But pulling them out like this means we don't
have to add a tree at the beginning anymore. And then
additionally, additionally, JavaScript has something called
syntactic sugar. So instead of doing this on two lines like
this, we can actually do that whole bit on one line. So
instead, we just extrapolate those two variables, right in
the function declaration. So this line is the exact same
thing as doing this line. This is an asynchronous, nameless
f
unction using the arrow notation, or working with our
deploy scripts here. And we're default, exporting it with
module dot exports. I don't know that was a lot. And another is
kind of a lot of syntactic sugar here. But if that's really
confusing for you, just feel free to use the above. And
whenever we refer to get named accounts, you can also just do a
three dot get named accounts, or a three dot deployments. So
hopefully, that's clear that this top part is gonna be the
same as this bottom part
right here, whichever one you feel
more comfortable working with. But alright, now that we've
gotten all that out of the way, let's continue with the script.
So we're using this deployments object, reason this deployments
object to get two functions, those two functions are going to
be the deploy function, and the log function. So we're gonna say
const, deploy log equals deployments. So we're going to
pull these two functions out of deployments. And then we're also
going to do const Deployer. E
quals await, get named
accounts. So we are grabbing this new deploy function, this
new log function, and we're grabbing this deployer account
from this weird get named accounts function. What's this
get named accounts function, this get named accounts is a way
for us to get named accounts. When working with ethers we saw
when working with ethers, we can actually get our accounts based
off of the number in the Account section of each network. So for
example, in this list of private keys,
private
keys zero private key one private key two, it might
get a little confusing to remember which ones which so
instead of working like that, we can add a section at the bottom
called named accounts where we can name each one of those spots
in the accounts array. So we'll do named accounts. And we'll say
one of the accounts that will name is going to be named
Deployer. And we're gonna say by default, the zero with account
is going to be Deployer. We can also specify which number is
going to be the de
ployer account across different chains. For
example, on Rigby we wanted the deployer account to be the first
position, we could do something like this or on hard hat, we
could do it like this. We can create multiple users. Like for
example, if we wanted to do a user for some test or something,
and we'll just say the default is one or whatever we wanted in
here. So back in our deploy fun me We're going to say we're
going to grab that deployer account from our named accounts.
And then finally, we'
re going to grab our chain ID for reasons
that will come clear pretty soon. So we'll do const, chain
ID equals network dot config dot chain ID. Now, how do we
actually deploy this fund me contract? Well, let's think
about this for a little bit. When working with remix, it was
pretty easy, right? We just deployed it to a test net. Ah,
that's kind of the issue there, isn't it deploying to a rink,
the test net is a little bit slow. We don't always want to
have to deploy to one of these slow test ne
ts or even a main
net when tinkering and fiddling with our contracts, do we know
that's gonna be really bad, we really want to deploy to a test
net as a last stop after we've done all our testing locally. Or
we can deploy it to a test that to see some very specific code
work, like for example, with the chain link documentation. So
ideally, we deploy this to a local network first. But can we
just do that? Well, if we look in our price converter, dot
Seoul, we have this hard coded address in here,
this 0x
address, if we go to Doc's dot chain that link EVM chains,
contract addresses for Aetherium data feeds. That address is the
eth USD, specifically for Rinkeby. What if we work on the
Hard Hat Network? Example? default network? Hard Hat. And then like I said
before, if you don't write this in part, it is automatically the
default network. But if we're to point to the Hard Hat Network,
harder network is a blank blockchain. And it gets
destroyed every time our scripts finish, or even if we'
re working
with a local node, this price feed contract won't exist, one
of the code there won't be updated with data. So what do we
do? How do we test and interact with our code locally? Is there
a way we can do this? Well, one of the ways that we can do this
that we'll learn a little bit later is actually forking a
blockchain, where you can keep stuff hard coded. But usually,
it's still better to figure out how to do everything with
something called mocks. There's a great Stack Overflow questio
n
that just says, What is mocking and mocking is primarily used
for unit testing, which we'll talk about in a little bit. And
object under test may have dependencies on other complex
objects. To isolate the behavior of the object, you want to
replace other objects by mocks that simulate the behavior of
the real objects. In short, mocking is creating objects that
simulate behavior of real objects. Now, this might seem
like a lot of words. But basically, what we want to do is
we want to make a fak
e price feed contract that we can use
and we can control when working locally. So back here, I'm just
going to leave a note in here saying, When going for local
host or Hard Hat Network, we want to use a mock. Okay, great,
well, we can use a mock and we'll learn how to make one of
those in a little bit. Well, what happens when we want to
change chains, for example, back in dots dot chain to link EVM
chains, contract addresses, there are a ton of different
block chains that have price feeds on th
em. And on each one
of these blockchains, the eth USD price feed is going to be a
little bit different. For example, we're looking at ETH
USD, the address of eth USD for Aetherium main net is different
from the address of eth USD for Rigby, which makes sense,
they're totally different contracts on different chains,
they have very similar functionality. And they do
nearly the exact same thing. But they're still different. We're
also going to need a way for us to modularize or parameterize,
this a
ddress in here, so that no matter what chain we deploy to,
we don't have to change any of our code, we can always have our
code be exactly the same. And we don't have to come in here and
like flip values and flip variables and stuff. So let's
keep that all of that in mind as we write the rest of this. Now
in order to parameterize. This, we actually want to prioritize
and do a little refactoring of our fundamental soul.
Refactoring basically means going back and, and changing the
way your code wo
rks. Right now we have this constructor
function, right the constructor function is the function that
automatically gets called whenever we deploy our contract.
Right now it's not doing a whole lot right now it's just updating
the owner variable to be whoever sent in the contract. But we can
actually have a do much more than that. Since this
constructor is a function just like every other function, we
can actually have it take parameters, one of the
parameters that we might like for it to have i
s going to be
the address of a price feed. So let's go ahead and add this and
figure out how to refactor all this code. So we're going to add
constructor address, price feed for the constructor in here.
When we deploy our contract. Now we're going to pass it the eth
USD price feed address depending on what chain we're on. If we're
on rink B, we'll use this address if one polygon will use
a different one b&b, different one, Gnosis hecho, avalanche,
etc, you get the picture. So we're going to marg
inalize this
like so. Now that our constructor takes a parameter
for the price feed, we can actually save an aggregator v3
interface object as a global variable in our price converter,
we just create a price feed variable of type aggregator v3
interface, which again, we're importing from the chainlink
repo, which is an interface object which gets compiled down
to the ABI. If you match an ABI with up with an address, you get
a contract that you can interact with. So we're going to do the
same thi
ng here. We're gonna say, aggregate Tor, v3
interface, public price feed, ration and call this price feed
address so that these don't have the same name. And in our
constructor, we're going to say price feed equals, and we're
going to do the exact same thing we did with our price converter
equals aggregate tore the three interface of price feed address. Like so
now, we have this price feed address that's variable and
modularized, depending on whatever chain that we're on.
Now, what we can do is
we can grab this price feed address,
and we can use it for our price converter. So where are we using
our price converter? Well, just a quick reminder, we're using
using price converter for you at 256. We're using this as a
library on top of our unit 256 type. So we're calling message
dot value dot get conversion rate. So we look at our price
converter, we have this function get conversion rate, which takes
an FML as its initial parameter, which again, since this is a
library, it automatically p
asses the message dot value into this
get conversion rate function. But we could also pass in this
price feed, and therefore we wouldn't need to hard code it in
the get price anymore. So let's go ahead and figure out how to
do that. Well, what we can do is we can do message dot value dot
get conversion rate, we'll stick price feed in here. And then
we'll have to update our get conversion rate to do a comma so
that it takes a second parameter, because remember,
again, the initial parameter is goi
ng to be message dot value.
And the second parameter is going to be what we define here.
So we'll do s amount, comma ag reg gate Tor v3 interface. And
we'll call this price feed. And now, when we call our get price
function, we can pass the price feed to the get price function.
And up here we can have get price. Take, you guessed it an
aggregate or the three interface called price feed. And now we no
longer need to hard code in the price feed. And we can just
delete those lines and have it compi
le like this, which is
awesome. So quick refresher, we're parameterizing that price
feed address and passing it in with a constructor that gets
saved as a global variable to an aggregator v3 interface type, or
passing it to a get conversion rate function, which passes it
to the get price function, which then just calls latest round
data. And we probably could have made this even easier, probably
could have just got rid of the get price function and stuck
this code in the get conversion rate. But
we'll leave it there
for now. Now that we've done that refactoring, let's make
sure it works. Yarn Hardhead, compile, invalid value undefined
for hardhat dot config dot networks. Let's go to the let's
go to the config real quick. That's because the default
network needs to be outside of networks. My mistake. Let's try
that again. I spelled interface wrong and the price converter.
And a quick note, if gives you an error like this, oftentimes,
you can command click or control click and open that
file up
right in the editor, which saves you some time, we're going to
have defined the line and find the file. But yeah, let's spell
that correctly. And let's try this again. And awesome. It
looks like it's compiling correctly. And we just have some
warnings, it looks like these warnings are just about this git
version, which is because we're shadowing this, we're creating a
new price variable down here, you've no we just created a
global price free variable. Let's just go ahead and delete
the
get version function altogether since we're not even
going to really need it. And we only use the Git version to show
you how to actually start working with interfaces. And
then we'll compile it one more time for good measure. Boom
compiled successfully. Awesome. So now we've just refactored our
code. So we can pass a price feed address depending on the
network that we're on. Okay, great. With all that being said,
let's come back to our deploy Funmi script, and let's learn
how to actually deploy
the rest of it. In order for us to deploy
a contract we remember from our last sections that we use the
contract factories, with heart hit Deploy, we can just use this
deploy function. And to use the deploy function, we'll say const
Funmi, which is going to be the name of our contract, equals
await. And we'll call this deploy function, the name of the
contract that we're deploying right now, and then a list of
overrides that we want to add here. So we're gonna say who is
actually deploying this
by saying from, we're gonna say
it's from the Deployer. We're gonna pass any arguments to the
constructor In this args piece here, which we just added a
single Argh. So these brackets, we're going to make it a list of
arguments, we're going to put the price feed address in here,
which we'll show you how to do in a second, put price feed
address. And then we're also going to do some custom logging
here so that we don't have to do all that console dot log stuff
that we've been doing this whole ti
me. And we need to put
something in here. We need to put an address in here. And you
can use this backslash star to put like a common in between
your code. We can't just do const address
equals, you know the address and stick it in here. Well, I mean,
we could but we're not really prioritizing now. Right? We're
kind of back to just hard coding it here. So what can we do
instead? Well, what we can do is we can actually use the chain ID
to do something like if chain ID is x, use address, Y, or if
chain ID is Z use address A. So we can do something that looks
like this. And to enable this functionality, we actually take
a page out of the Ave GitHub. So Ave is another protocol that's
on multiple chains and has to deploy their code to multiple
chains and work with multiple different addresses. So what
they do is they use a number of different useful tricks. But one
of the main ones is using this helper hardhat config. Now
they're using TypeScript with JavaScript, but it's gonna be
the same
thing. With this config, they have different
variables, depending on what network that they're actually
on. And depending on the network that they're on, they use
different variables. So they use this network config almost to do
exactly what we're trying to do here. So what we want to do is
we're going to create a new file at the root directory, so just
click down here, new file. And we're going to call it helper,
hard hat config dot j, s. And this is where we're going to
define that network con
fig. And this is where we're going to
say, hey, if you're on network, a, use this address network, be
this use this address, etc. So we're going to create an object
called const. Network config. equals and we're going to add a
bunch of stuff in here. So our main network that we're working
with right now is rinky. dinky has a chain ID of four. So we'll
say chain ID four is going to be named Rinkeby. And the eth USD
price feed address is going to be the price feed address of
rank B of the eth USD
price feed. So we're going to copied
from the documentation or from the GitHub, whatever you want to
do, and paste it in here. Now we have a simple methodology of
keeping track of different price feeds a different contract
addresses across different chains. Let's say for example,
we wanted to deploy to Polygon as well. Well, first, what are
we going to need? Well, we're going to need the chain ID of
polygon. So a quick little Google Search brings us to the
polygon documentation. And we see the c
hain ID is 137. So I'll
do 137. What's a little brackets here, we'll say name, polygon.
Then we'll do a comma eth USD, price feed. And then we'll add
the price feed of eth USD on polygon. So docstoc, chain link,
polygon or Matic and then we'll look up eth, USD. And boom, we
see it right here. We've grabbed this address, and we paste it
in. Well, what about the Hardhead? Network? We'll get to
that in just a second, don't you worry. And then at the bottom,
we need to export this network config. So
our other scripts can
actually work with it. So we'll do module dot exports equals
network config. And we're going to actually export a couple of
things from this file, which is why we're doing it like this
instead of that default way that I showed you before. So back in
our script, now, what can we do? Well, first, we want to go ahead
and import that network config. So we'll say const. Network
config equals require, and then we'll import it, we'll go down
and directory to help our Hardhead con
fig and save. And I
just want to mention this one more time, just so that it
doesn't confuse anybody. This syntax here, constant network
config with the little curly braces around it is the same is
if I went const. Helper config equals this thing, which helper
config is now kind of this whole file. And then const network
config equals Hopper config dot network config. So again, this
index is just kind of an easy way to extrapolate or pull out
just the network config from this file. So that's how
that
works. And that's why we export it at the bottom so that we can
do this, please use the GitHub repository to ask questions and
discussions especially about some of this JavaScript stuff.
Alright, great. So now that we have this network configured
here, we can now do this part of where we say if China d z use a
if chain ID is x use y. So Since our helper config is nicely in
this kind of dictionary, key value pair style, what we can do
is we can say const. eth USD. Price feed address equals
network config at the chain ID, because if Francina D, or it'll
be this object, French entity polygon, it'll be this object at
the eth USD price feed, we're going to save this to eth USD
price feed address. And now no matter what chain we're on,
whenever we run hard hat deploy, if I run yarn, hard hat deploy
dash dash network Rinkeby. This chain ID is going to be four.
And so it's going to use this price feed address. If I do dash
test network polygon, and I remember to add both ring P and
polyg
on to my networks, like here, the channel is going to be
137. It's going to use this price feed address. So this is
awesome. This is exactly what we want. But is it everything that
we want? Those of you who have been questioning while I have
been coding and talking, you might be thinking, Okay, well,
you talked about this marking thing. You talked about
localhost and hard hat. And how do we test this locally? Like
this is how we go to a test net and a main net. But what about a
local network? An
d that is exactly what we're going to talk
about now. So we've modularized, our code and parameterize our
code so that we're going to use the address based off of the
chain that we're on. But what if we use a chain that doesn't even
have a price feed address on it? What do we do there? This is
where we actually create those mock contracts. The idea of mock
contracts here is if the contract doesn't exist, we
deploy a minimal version of it for our local testing, or our
local testing. And deploying
mocks is technically a deploy
script. So what we do actually is back in our deploy folder is
we're going to create a new file, and we're going to call it
00 Dash deploy mocks dot j s, we started with 00. Because this is
almost like the pre deploy stuff, we only do this,
sometimes we don't always deploy mocks, right, we don't need to
deploy mocks to Rinkeby, or polygon or or Aetherium main
net, because those already have these price feeds, we're
actually going to deploy our own mock price feed c
ontracts. And
in our deploy Funmi script, we're going to use our own
contracts, instead of already established contracts. If we're
on a network that doesn't have any price, few contracts, like
hard hat or locos, for example. So let's write our deploy mock
script. So the setup of this is going to look nearly identical
to our deploy Funmi. And again, if you want to set it up like
this, you absolutely can. But I'm actually just going to copy
this, this part, paste it in here, because that initial p
art
is going to be exactly the same. Oh, and over here, I just
realized that we're calling this network thing without being
defined, JavaScript will kind of be smart enough to know where
this network thing is coming from. But it can be a little bit
confusing. So it's better to be really explicit, and say, const.
Network equals require hard hat, this network thing is coming
from hard hat. And we're going to grab this line. And we're
also going to use this at the top of our script here. And then
o
ur top section is going to look exactly the same as well, we're
going to grab these three lines, and paste them in deploy
deployer chain ID, boom, it's all going to be the same here,
because we're setting up to deploy some stuff. Now we want
to deploy a new contract. But if we look at our contracts folder,
this is all we have right now. So we're going to need to add
this mock this fake contract to our contracts folder. Now what
we can do is in our contracts folder, we want to separate this
file
from the rest of our file so that we know okay, this isn't
part of our project, but it is part of our testing. So we're
going to right click Create New Folder. And we can either call
it mocks, or test I like to call mine test. And inside of this
folder, we can go ahead and right click create a new file,
I'm going to create a new file and call it mock, v3 aggregate
tore that soul. And this is where we're going to define our
mock price feed aggregator ourselves. So how can we create
our own fake p
rice feed contract, so we can test
everything locally? Well, one thing we could do is we go to
the chain link GitHub repo, and go through the contracts and
find one of these price feed addresses, source eight, or
maybe we'll go back to source we'll maybe we'll check in V
six. Looks like we could find some
stuff and look around and we probably copy paste all this
code, but it really seems like kind of a huge pain in the butt
to have to copy all this code. Now we absolutely could we copy
paste the
code in here, but we're gonna do something a
little bit more clever. So the chainlink repo actually comes
with some mocks. If we go to contracts, SRC V 0.6 tests They
actually have a mock v3 aggregator dot soul in here that
we can use as our mock. So we can copy paste everything, but
we'd have to revamp a little bit of it because it's doing some
dot dot stuff. It's talking to other contracts that are locally
in this file structure that are not going to be in our file
structure. So instead thoug
h, what we can do is we can use
this node modules package to our advantage, we can just say
pragma, solidity, carrot zero, point 6.0, we'll use the same
version that that package is doing. And then just do import
at a chain link slash contracts slash SRC slash v 0.6. Slash
tests slash mock, B, three egg, Reg, gay tore that soul. And
then we'll add, and then of course, we'll add spdx, license
identifier, MIT. And boom, this is actually all we need. If we
just import the code like this, remember,
this is exactly the
same as copy pasting this contract into our project, of
course, with this path resolve to where it actually is in our
node modules. Now, actually, I can run yarn hardhat compile,
and it will also compile this contract. Except for of course,
we have an issue, hey, compiler versions don't match. Right?
What's What's up with that now, you're going to get into
situations where you will be working with contracts that are
not the same version of solidity as you why well because
con
tracts keep being deployed all the time. And there are a
ton of contracts that are in version 0.4, solidity,
0.5 0.678, and probably 910, or 15 billion, or however many
solidity versions will come to be. So in our config, in our
hard hat dot config, when you scroll to the bottom, we can
scroll to where we're defining our solidity version. And we can
actually add multiple solidity versions, so that our compiler
can compile multiple versions of solidity. To do that, we'll say,
so Lyd, did T. And w
e'll turn it into an object here, we'll make
sure to put this comma here. And inside our solidity object,
we'll put compilers and we'll have a list of compilers. Our
first one we'll say is version, zero, point 8.8. And we'll say
our second one is going to be version 0.6, point six, and then
we'll go ahead and save that. And it looks like mine wanted to
format it like this, which is fine. Now, we can go ahead,
rerun, yarn, Hardhead, compile, and boom, compiled five solidity
files successfully. Th
is means that our Mark V aggregator
should also have been compiled. And if we look in artifacts, at
chain link, do indeed see this at chain link slash contract
slash SRC bid, and a v 0.6. In tests, we see this mock
aggregator dot soul, which has been compiled. Awesome. So now
that we have our mock contract compiled, we now have a contract
that we can use to deploy a fake price feed to a blockchain. So
how do we actually do this? Well, it's going to be the exact
same way that we deployed the Funm
i contract. But we're going
to add a little if statement in here, we don't want to deploy
this mock contract to a test net or a network that actually has a
price feed on it, we could just do something like if chain ID
does not equal, you know, some chain ID, then deploy marks,
right. And then this is kind of pseudocode. Obviously, this code
won't actually work. But instead, what I like is I
actually like to specify which chains are going to be my
development chains, which chains are going to be
the one that I
can deploy these mocks to, in my helper, hard hat config, I'll
define these chains. So I'll say const, development chains,
equals, and then I'll just say hard hat, and local host. I'll
export these. And back in my deploy mocks, I'll import these
with const. Development chains equals require dot dot slash
helper Hardhead config. And now I'll say if development chains
dot includes chain ID. This includes keyword basically is a
function that checks to see if some variable is inside a
n
array, then we're gonna go ahead and deploy Max, and which is
what we want to do. So we'll do log, which we're getting from
deployments, which is basically console dot log. And we'll say
local network detected, deploying mocks. And we'll do a weight ploy, and
we'll deploy our new mock v3 aggregator mock v3 aggregator
will do a comma. There a little colons here. If we want to get
really specific, we can say contract v3 aggregator which
we're kind of already saying we'll say from deployer We'll
say logging is going to be true. And then we need to pass some
arguments, we need to pass the constructor parameters for the
mock v3 aggregator, which are what? Well, let's go to docs
chain to link to find out. Or you can also just go to Node
modules chainlink, SRC V 06, tests, and then all the way down
to mock V three, aggregated out. So where you could also find the
constructor in here, whatever one you like better, sometimes I
find it easier just to read GitHub Ctrl plus F or Command
plus F f
or constructor. We see it takes a decimals and an
initial answer. And if we read through the code, we'll learn
that the decimals object is going to be equivalent to the
decimals parameter is going to be equivalent to this decimals
function and the initial answer. And the initial answer is
basically just going to be what is the price feed starting at,
we actually get to pick the price of the price feed, which
works out really well, because that works out great for
testing. I usually like to defin
e the decimals and the
initial answers somewhere outside of this function so that
I can access it later. One good place you can add it is once
again in our helper Hardhead config.js. So I might do const
decimals equals eight. And then const initial answer. Answer
equals, and we'll do 2000. So since we have a decimals, we'll
do 2000 And then 123456788 decimal places, and then we'll
export these as well. Export decimals, and export initial
answer. We could of course, just do you know, const decima
ls
equals eight at the top and then initial answer and then use them
down here. But I like to do it like that. So Conce. So now we
have to import them in here. const development chains. It's
also grabbed decimals. It's also grabbed initial answer, we'll
save it. We'll take a look back at the constructor looks like
it's decimals first, initial answers second. So in our
arguments, we'll do decimals first, initial answers second,
and tada. And then we will be all done that we'll do a quick
log box
deployed. And then I also like to do kind of like a
big line at the end of all of my deploy scripts just to be like,
hey, that's the end of this deploy script. Anything else
after this is going to be a different deploy script. All
right, great. Now our deployed mocks script is actually done.
But our deploy Funmi script isn't quite done. Is there a way
that we could run only our deploy mock script? Well, yes,
there is. Great, thanks for asking. What we can do at the
bottom of our deploy mock scri
pt is we can add a module that
exports dot tags, equals, and we'll say all and marks. Now
what we can do is if we run yarn, hard hat deploy, we can
add this flag dash dash tags. And it will only run the deploy
scripts that have a special tag. So we'll run our mocks tag,
which means it'll only run our deploy mock script. And, and
oops, actually, in our helper config development chains is
actually hard hat and localhost. And I said, we're going to try
to do with the chain ID, sorry, we're going to
do this, we're
gonna do development chains dot includes network dot name. Because our helper config is
using names and not chain IDs, so if development chains that
includes that network the names, then we're gonna go ahead and
deploy the mocks. So let's go ahead and run this yarn here and
hit Deploy dash dash tags, mocks. And perfect we do indeed
see our mocks getting deployed here. This log true means that
it's going to spit out stuff like this. It'll say contract,
it's deploying, it'll say th
e transaction it's doing and it'll
say where it was deployed with how much gas and awesome This
means our deployed Mach script is working perfectly. So now we
have our deploy mocks script working perfectly. So how do we
apply that back to our deploy Funmi script? Well, we're gonna
do the exact same thing here. Instead of making eth USD price
feed address constant variable, we're gonna say let at USD price
you'd variable so that we can actually update it. And we'll
say, if development chains dot
includes network dot name, what
we can do with hard hat deploy is we can just get the most
recent deployment using a command called literally get. So
we'll say const. eth USD aggregator equals await
deployments dot get. And then the name of the contract that we
deployed Mach v3 aggregator, and if you wanted to just do get
instead of deployments dot get, we absolutely could, just by
doing it like this. Those are exactly the same. So we'll get
the address like this and then we'll say eth USD price
feed
address equals that eth USD aggregator contract dot address.
And then if we're not on a development chain, if we didn't
deploy a mock, we're just going to do exactly what we did
before. With using the network config. Oh, my goodness. Now,
now that we've done all of these steps, let's add a little log
thing at the bottom here with just a bunch of hyphens. Now, we
should have a very robust script to flip between a local
development chain, a test net chain, a main net chain, and
allow us to d
eploy literally everywhere without changing any
of our solidity. And then we just take this, this eth USD
price feed address and stick it into logs here. And then at the
bottom, we can do module dot exports, dot tags equals and
then we'll just do all and then we'll call this one Funmi. Oh,
now moment of truth. If we did all this, right, we should just
be able to run yarn, hard hat deploy. And it should work on
our local chain, our hard hat chain. And then it should also
work on any test net that
we give it. So let's give this a
try. Yarn, Hardhead deploy. Let's see if this works.
Awesome. And we got this all to deploy locally to our Hard Hat
Network, we can see that we went ahead and we deployed mocks, we
did our little underline here. And then we deployed Funmi
deployed at this address with this much gas. Now, what are the
other awesome things about hard hat deploy? When we run our
local blockchain, our own blockchain node, hard hat deploy
will automatically run through all of our dep
loy scripts and
add them to our node. So now if I run yarn, hard hat node, we're
going to spin up a new blockchain node, but it's
already going to have all of our deployed contracts on it. So
every time we spin up a local node, now it's going to come
automatically packed with the contracts that we want on it. So
we are going to show us doing this on a test net on Rinkeby.
But before we actually test it on Rinkeby, I'm going to add a
little bit of the auto verification piece in here as
well, beca
use we did that in the last lesson. And we wanted to
show how to do it in hard hat deploy as well. So right after
we deploy our Funmi, we can do something similar here, we'll
say, if developer chains includes network dot name, we'll
say if developer chains doesn't include network dot name,
because we don't want to verify on a local network. So we'll say
if not development chains dot includes network dot name, the
exclamation mark, aka the bang means not when we're talking
about booleans. So we'r
e saying, if the name of the network isn't
a development chain, we want to go ahead and verify and same as
last time. And if process dot e NV dot ether scan API key, then we're going to go ahead and
verify. Now before we had our verify code, right in our deploy
code, we're gonna do something a little bit different here.
Instead of having our verify code in our deploy scripts here,
we're actually going to create a new folder called utils, which
stands for utilities. And this is where we're going
to add
different scripts that we can use across different
deployments. Because let's say we have 50 Deploy scripts, we're
not going to make 50 Deploy functions, we're just going to
add them to our utils folder, and in our utils folder, or
create a new file called verify dot j s, we're going to add that
code from our last project in here. So if you want, you can go
ahead copy paste from our last project over to this one, or you
can pause the video to type it out yourself. Since we're using
the ru
n command here, we're gonna do const run equals
require RT hat. And then at the bottom, we're going to do module
exports. Exports equals verify. Now that we have a verify script
in our utils folder, back in our deploy Funmi we're going to say
const. Verify equals require dot dot slash utils. Slash verify.
And since now in our verified Jas, we have a lot of this
trycatch stuff in here, we can just do a wait. Verify and a
verify once again takes a contract address and a list of
arguments. We'll sa
y await verify, fund me dot address and
then the list of arguments. To make the list of arguments
easier to put in. You can go const args, equals and then
we'll just stick our eth USD price feed in here and then
replace this with args and then take this args and pop it on
down into the second parameter here. All right, great. Now
let's go ahead and deploy this to With the Rinkeby test net,
and what do we need to deploy this to the Rinkeby test net?
Well, let's jump into our hard hat config first
. And let's
clean this up, we don't really need this accounts task, so I'm
just going to delete it, I don't really need this comment. So I'm
going to delete this too. And let's jump into the network
section, we're not going to be working with ropsten. So we're
gonna go ahead and dump that. We are however, going to be working
with rink B, the URL is going to be that same Rinkeby RPC URL. So
we're going to define that up here, like we did before. And if
you want to copy paste from your last projec
t, feel free to do so
you can also follow along with me or fastforward me the counts
is going to be the same. I'm going to go ahead and just copy
paste the gas reporter with what we had from before. So we're
going to add this constant corn market cap API key equals
process dot EMV dot corn market cap API key. Do the same thing
with the ether scan API key Licious add everything in here.
Now. We have our ether scan section in here already, we're
gonna have our gas reporter be false, because I don'
t really
feel like using it right now. And then finally, we're going to
add one more thing in here. Remember how in our last
project, before we actually verified we waited some block
confirmations. That way ether scan could make sure to catch
up. Well, we can do the exact same thing in here in our hard
hat dot config, we can add a section for each test net for
how many blocks we want to wait, I'm going to add block
confirmations of six. Now back on our deploy Funmi. In a new
section, I can add w
ait confirmations of network dot
config. That block on for motions, or one is or one means
if no block confirmations is given in our Hardhead dot config
will just wait for one block. And again, the reason we want to
wait for approximately six block confirmations is we want to give
ether scan a chance to index our transaction. And I added a chain
ID 42 when it should be for my mistake. And of course, we're
going to need our dot env file, where we add all of our stuff
from the last session, the RI
P ERP CRL private key ether scan
API and then coin market cap API. All right moment of truth.
Let's try this out. If we run yarn Hardhead deploy dash dash
network Rinkeby. It should not deploy any marks because we have
this if statement in our mock deployment. But it should deploy
our Funmi contract using the correct price feed address. And
then it'll go ahead and verify it. Since we're waiting for six
block confirmations, we can even be super secure by adding dash
dash tags and just running the
funding tag. But we're just
going to do yarn hearted deploy network Rigby. And let's see
what happens. All right, and we're deploying funding. And we
can see the transaction that we have for fun me, this is that
logging feature, we have log is true for deploying Funmi. So it
gives us the transaction once it has a transaction, and it will
give us the address once we have the address. So we're going to
wait six block confirmations for this transaction to finish going
through. Now we see we've dep
loyed this contract address
with X amount of gas. And now we're running the verification
process. While the verification process is running, we can pull
up Rinkeby ether scan, paste our address in here and see that we
have indeed created this contract. And now it looks like
we've successfully verified the contract on ether scan. So if we
hit refresh, we can indeed see that the contract has been
verified. Awesome. All right. So this is fantastic. Our deploy
script is looking great, we're able to
deploy to a local chain,
we're able to deploy to a test that and if we wanted to, we
could deploy to any network that we wanted simply by updating our
hard hat config, and then updating our helper config. This
is fantastic. Great work so far. Now we're
about to jump in and level up our tests. But before we do
that, we're going to clean up our Funmi contract a little bit
to make it look a little bit more professional. And I'm going
to talk about some of the syntax and some of the reasons why some
conventions exist. We're not going to do this full force on
all the projects moving forward. But they are good to know and
they are good to keep in mind when moving forward and working
with our contracts. While we go through this we're going to
learn why some of these conventions exist including
learning a little bit of low level solidity. So don't skip
this part. When we get to later sections. We're going to be a
little bit looser and not be as strict with the code style
guides but That's basi
cally what we're gonna go over now. And for
now, you might see this event funded thing here, please just
ignore that for now, in an earlier take, I'd introduced the
events much earlier. And now we're actually going to learn
about events a little bit later in the course. So please ignore
that event funded for now. So let's go ahead and tweak a
little bit of our contracts here. Now what I'm talking about
tweaking this to make it look professional, a little bit more
professional, I'm talking about
the solidity style guide, there
are some conventions for naming and ordering that we can follow
to make our code look a little bit better. Now, like I said,
this is going to be a little bit more optional, because it can be
a little bit verbose. And it doesn't really make that big of
a difference. But it can increase readability of your
contracts by a lot and make your code look a lot nicer. So if you
want, you can go through this style guide to learn more about
what kind of makes solidity look n
ice, and what makes it not look
nice. But well, that's some of these style guides in here,
we're not going to follow the style guide exactly to a tee.
But we are going to make some best efforts to make our code
follow the style guide, we've got a link to the style guide in
the GitHub repository for this section, we can read some more
about the layout. But the main thing we want to look at is this
order of the layout, we want to start with our imports with our
pragma statement, our imports, inter
faces, libraries, and then
contracts. And then inside each contract type declarations,
state variables, events, modifiers, and functions. So
let's go back here and make sure that we're up to speed, we want
our pragma first. Alright, awesome, we did exactly that,
then we want our imports. Okay, awesome, we have those too.
Something that's not in the style guide is going to be error
codes, which we definitely want next. So next is going to be
error codes. Now this is where we're going to bump into
one of
our first updates here. As of recent, it's sort of becoming a
best practice to add the name of your contract, some underscores,
and then the name of your error. Whenever you're running into an
error. This makes it a lot easier to understand which
contract is throwing the error. So for this, we're going to say
error Funmi, two underscores not owner, then we're going to
scroll down to the our revert and set it like this. This way,
when if we ever run into this error, we know that the error
is
coming from the Funmi contract, not from the aggregator v3
interface, or the price converter or some other
contract. So that's how we want to write our error codes here.
If we had any interfaces or libraries not imported, we would
add them here. But then finally, we add our contracts. In this
file, we only have one contract here. It's our Funmi contract.
Awesome. Now the next thing we want to learn about as far as
style guides go is this thing called natspec. natspec stands
for Etherium natu
ral language specification format. And it's
basically a way of documenting our code inspired by Doxygen
uses Doxygen style comments and tags to help document our code,
you can click the link here in the solidity documentation to
learn more about Doxygen. If we scroll down in the
documentation, here, we can see an example of using natspec.
Whenever we have a contract or a function that needs
documentation, we can add some syntax that looks like this to
it. So for example, if we wanted to add this
to our code, we
could add a comment explaining this funding contract to start a
piece of natspec, you can do three backslashes, or one
backslash, two stars, and then another ending star here.
Everything we put inside of this comment section basically gets
turned into a comment. For the start of our contract, we'll do
the Add sign title to explain basically, what this contract
is, it's fun, we contract is going to be a contract for crowdfunding, we'll add
another star. And we can add the author
of it, which is going to
be your name, I'm going to put Patrick Collins, of course, then
we'll add a notice, which is just kind of a note to people we
can say this contract is is to demo a sample funding contract.
And we can also add apt Dev, which is a note specifically to
developers. And we can say this implements price feeds as our
library. The reason that we want to add these tags here is
actually because we can use the natspec to automatically create
documentation for us if we download so w
e can actually run
solc dash s user doc dash dash dev doc, and the name of our
file to automatically generate documentation. So this is also
really helpful for automatically creating documentation for other
developers who interact with the protocol later on. You can use
this natspec For as many or as few functions as you'd like.
Most of us probably aren't going to be making documentation. So
we really just want to follow those guidelines if we think
some function or some section of our code is a
little bit tricky
for other developers. Now that we're inside of contract, we can
follow the order of our contract. We're first going to
start with type declarations which we don't really have any
except for the fact that we're using our price converter for
the UN 256 type. Okay, great. You Next, after our type
declarations, we're going to do state variables. And in this
state variables section, this is where we're actually going to
change the name of some of our state variables. So we'll do a
little common here state variables. Now in the solidity
style guide, kind of adhere to the naming styles, we use upper
and lowercase, we use total caps with underscores here. However,
these naming variables are going to change in the future in this
section. And if you're following along with the GitHub repo
associated with this course, these are going to be actually a
little bit different than what you see. However, for now, we're
going to leave them as they are, because the reason why we're
goi
ng to change them isn't going to be quite clear yet. Don't
worry. So these names are going to change soon, but not yet.
Alright, after state variables comes events, and modifiers. We
don't have any events, but we do have a modifier. So we'll copy
this. And actually, we'll delete this comment here. And we'll
paste our modifier here. Oh, and it looks like looks like we're
not auto formatting here. So we're going to uncomment
immutable actually, so that it automatically auto formats.
Okay, great. W
e're out of formatting now. And Cool.
Alright, so now we have our modifiers. Next, we have right
here, and then we have all of our functions. Great. We
actually want to group our functions in this order that I
just print here. So we want the constructor which we have
received and fall back, we do have fall back and receive. So
we're going to actually copy those and delete this comment.
We're gonna stick those veterans Heath here looks like receive
goes first. So we'll put that here. Then externa
l functions,
then public, internal, private. So we have public public. And
that's it. And then we can delete this part down here.
Okay. Cool. And if we want, we can do that syntax up here from
the net spec for our functions. For example, for fund it, we
could even just copy paste, we would remove title, we would
remove author, and we just say add, notice, this function,
funds this contract. And we can even leave a little dev thing
here to talk about it. Now if we have parameters, you can do apt
puram. And say like what the parameter is. And then if we
have returns, we can say returns, or returns and then
what it returns for the documentation. Since this
doesn't have any parameters in here, and doesn't return
anything, we can just leave it like this. And great, we've just
revamped our contract here to make it a little bit more nicely
formatted. Great job. Now, like I said, we actually are going to
change the names of our state variables, and we're going to
add some functions in here and
a little bit. So if we're
following with the GitHub repo, the state of the contract right
now is going to look a little bit different. But it'll make
sense why change this up in a little bit. Alright, so now that we've
cleaned this up, we've got to deploy marks, deploy Funmi.
Let's go ahead and start writing some tests. And after we write
these tests, we're actually going to run that gas estimator
and using that gas estimator, we're gonna go back, and we're
going to update this contract one mor
e time to make this even
cheaper to use and work with. And remember, that's going to be
one of the advantages of writing these tests is how we can
optimize our contracts to be even faster, more gas efficient,
etc, we want to make sure that we write really good tests, and
this is going to be one of our first jumps into these more
professional test setups. So we're going to jump into our
test folder, we're going to delete this sample test.js. In
our last section, we went over a really minimalistic
test, which
is great. However, when we get bigger and bigger projects,
we're going to want to start testing more and more different
things, we're going to get more and more into at least two
different types of testing. So if we cd into our test folder,
we're going to make one directory called staging. And
then we're going to make another directory called unit. And now
if we look in our test folder, we now have a staging folder,
and a unit test folder. Now we're going to talk about two
different
types of tests. The first one is going to be
something called a unit test. Now what is a unit test? Unit
testing is a software testing method by which individual units
of source code are tested. Basically, what we want to do is
in our contracts, we want to test minimal portions of our
code to make sure that they work correctly. Then once our small
pieces of the test work, we want to do a staging test or maybe an
integration test. This might be where we run our code on a test
net or some actual
network. You can think of a staging test,
it's kind of the last stop before you deploy to a main net.
They're not always 100% necessary, but they can be
really, really helpful. Remember, we do want to be
conscientious of how much we use our test nets, but we absolutely
would 100% want to make sure that everything works locally
and that we unit test and we run all of our code locally. Then we
can use staging tests on an actual test net to make sure
that our code will work with actual other contra
cts. Now unit
tests can be done with local Hard Hat Network or a forked
Hard Hat Network, we'll talk about this for tar Hat Network
very soon, right, now, let's build these unit tests, these
unit tests are going to be basically what we saw in our
last section. So let's go in and let's jump in and write some of
these unit tests. So let's create a new test, we'll call it
on me dot test dot j, s. And we'll start making our tests in
here. Now, we did test previously in our last section,
but our test
s here are going to look a little bit differently,
we're actually going to use hardhat deploy, to automatically
set up our tests as if both of these deployed functions had
been run. So let's go ahead and get this started. So we're still
going to do that same setup that we're gonna do describe, we're
gonna say fund me. And this is going to have that async
function like so. And in here, we're going to have a before
each, and we're going to have some hits and some describes and
everything. Now sinc
e we want to unit test this, we're gonna go a
little bit heavier on the test here, and then with our last
project, but in the future, we'll go a little bit lighter
with some of the tests. So let's get started. If we run yarn
Hardhead test right now, we're gonna get zero passing. Now, if
we run yarn hardhat coverage, we're gonna get something that
looks like this, saying, Hey, you're missing a lot of stuff.
So let's try to cover some more lines with our tests. And one
way we can do that is actual
ly we can group our tests based off
of different functions. So let's have our first set of tests be
around our constructor. To do that inside of our first
describe, we can add another describe, have this describe the
just the constructor, this larger scope will be for the
entire Funmi contract. And everything inside this one will
just be for the constructor. So this will also be an async
function. And these tasks will be just for the constructor. But
before we even work on this describe, we prob
ably want to
deploy our Funmi contract. So let's learn how to do that. So
we'll do a before each, which will be an async function. And
we're going to deploy our Funmi contract using hard hat deploy.
Since we use hard hat deploy, our Funmi contract will come
even with our mocks and everything. So above the before
each, let's do let me HDMI. And then
here, we're going to deploy Funmi, where we're going to
deploy our Funmi contract is first by pulling in our
deployments object from Harnett deploy.
So we'll do const
deployments equals require hardhat. And this deployments
object has, has a function called fixture with fixture does
is it allows us to basically run our entire deploy folder with as
many tags as we want. You'll notice I added this alt tag in
both of our scripts. This means that in this deployment stuff
fixtures, it's gonna be a wait deployment of fixtures. If I run
away deployments dot fixture, I'll run through our deploy
scripts on our local network and deploy all of the cont
racts that
we can use them in our scripts and in our testing, and we can
deploy everything in that deploy folder with just this one line.
isn't that helpful. Now, once all of our contracts have been
deployed, we can start getting them will say fund me equals
await ethers, and we'll pull an ether some hard hat as well. dot
and this is where hard hat deploy is helpful. Again, hard
to deploy rapt ether is with a function called Get contract,
this get contract function is going to get the most recen
t
deployment of whatever contract we tell it. So we'll say get
contract of fun to me. So this will give us the most recent Lee
deployed Funmi contract in just this one line. And now Funmi
will be equal to this line here. Now we're going to make a bunch
of transactions on our Funmi. To test it. Of course, we can also
tell ethers which account we want connected to fund me. So I
can say const deployer equals goes away get named accounts,
exactly like we did in our deploy scripts. And then we just
n
eed to import it from our tap in our deploy scripts we
imported get named accounts inside of our input parameters
for our deploy function. Remember getting named accounts
and deployments was abstracted from if we look up here, from
the hard hat runtime environment, and like I said,
the hard hat runtime environment is basically the same thing as
hard hat. So we can just go ahead and import it like this,
actually like this. Because we actually need to abstract just
the deployer from getting named
accounts. And now what we can do
is we can connect our Deployer to our Fund Me account. So
whenever we call a function with fund me, it'll automatically be
from that the player account, which is great. Another way you
can get different accounts directly from your heart had
config. Is you could take const accounts equals await ethers dot
get signers, ethers dot get signers is going to return
whatever is in this account section of your network. If
you're on your default network hard hat, it's goin
g to give you
a list of 10 fake accounts that we can work with you then of
course, can do something like const. Account one equals
accounts. More correctly would be account zero equals account
zero and work like that. We'll leave that comment that out,
just in case you need a reference to it in the future.
Okay, great. So now we have our Funmi contract. Let's go ahead
and write some tests for testing the constructor. And we're
probably going to want to use this deployer object down here.
So we'l
l do let deployer above. And we'll do something a little
finicky here. But we'll say deployer equals Wait, get named
accounts, dot Deployer. And we'll just wrap this up so that
we can just grab this deployer object and assign it to declare
like so. Now in here, we'll create our first test, we'll say
it will say it sets the Agra Gator addresses correctly. Comma
will have this be an async function. And we'll say const
response equals await fund me dot, let's get this price feed
here, fund me dot p
rice feed. And then we'll want to make sure
this price feed is going to be the same as our Mk V three
aggregator since we're going to be running these tests locally.
So we should get our mark three V three aggregator up top. Let's
do let mock v3 aggregate store. And we'll grab this address the
same way mockbee Three aggregator equals await ethers
dot get contract mockbee Three aggregator comma, we'll connect
this one to the deployer as well. So we'll want to say cert
dot equals cert dot equal re
sponse comma lock v3
aggregator dot address. And of course we'll want to say const.
Assert equals require gy. Import that from DJI. Okay, cool. Now, let's go ahead and try this
out. Yarn hardhat. Test. Oops, I spelled response wrong. Let's
try that again. Awesome. So this means that we are indeed
assigning the price feed address correctly to the Mach v3
aggregator. Okay, great. Awesome. I think for now, that's
all we really want to do for our constructor. Now these two are
kind of a nice to have
, I showed them more just to kind of demo
what they look like, we're going to skip writing tests for them
for now. And we're actually going to go ahead and delete
them directly from the contract. If you want to go ahead and
write some tests for them and leave them in your examples for
your learnings. You absolutely can pause the video and write
some tests for it if you choose so, but we're going to skip
them. Next though, we are going to move on to fund and running
some tests for fun here. So le
t's go ahead and write
describe fund. This will be an async function. And in here,
we're going to do a number of tests. So if we're going to go
line by line here, what's the first thing that we should look
at? Well, we should look at this require line we should write a
test to see if this contract actually does fail? If not
enough, eath is sent. So let's go ahead and we'll say it fails,
if you don't send enough eath have this be an async function?
Now, how do we test to see if something fails? R
ight now we've
done assert equals, but if something fails, we might run
into an issue. So for example, if I run await fund me dot fund,
but I don't pass any value to this transaction. Let's see what
happens. I run yarn Hardhead test. Well, our test is actually
going to break VM exception. While processing transaction
reverted with reason string, you need to spend more eath. So our
tests are going to break which is good. We want this to break.
But we want to tell our test that this is okay. Right
? Want
to tell that this is okay. So the way we can do this, and this
is where our waffle testing comes into play. With waffle and
with testing, what we can actually do is we can use the
expect keyword and expect transactions to be reverted and
for transactions to fail. So instead of using assert here,
we're actually going to run a wait expect fund me dot fun 2.2
dot B, that reverted and we've actually even be more specific
here by saying to be reverted with and then the exact reverted
error, yo
u need to spend more eath. Now if we run our tests,
oops expect is not defined. So we need to import that from
Chai. Which chai is being overwritten by waffle, we see
that it does indeed Pass, which is perfect. So now we have a way
to both assert things and expect things to fail. Awesome, even
with the specific failure codes. Perfect. Let's write some more
tests here. Well, we probably want it to correctly update this
data structure. So we could say, it updates the amount funded
data structure,
it's going to be an async function. In here,
we're going to need to call fund v dot fund. However, we're going
to need to actually pass some value with this transaction. And
for now, we'll just hard code the value that we're going to
send, say const. Send value is going to be
112345 678-910-1234 5678, which is going to be one eath. Another
way we can write this though, is we can use the ethers utility to
make this a little bit easier to read. Because all those zeros
are kind of confusing, and it
's hard to tell at first glance
what this actually is. So we're gonna actually use ethers dot
utils dot parse ether, one, this parse ethers utility converts
this one into that one with 18 zeros, which makes life a lot
easier. If you go to the ethers documentation, there's also a
parts units function where you can actually convert any unit,
either ethers or Gwei. Or really whatever you want to do, you
could convert any number to any other Aetherium you type. So
this is the set value that we're go
ing to use for our fund. And,
and this is definitely going to be more than our minimum USD of
50. So after we call this fun function, we'll say const
response is going to be equal to a weight, fund me dot address to
Mt funded address to Mt funded for the deployer dot address.
Because remember, this is a mapping of each address and how
much they've actually funded. So if we use the deployer address,
it should give us the amount that we actually sent. So now we
can run assert dot equal response.to
string, right,
because this response is going to be the big number version of
how much has been funded by that account. And that should be the
same as our send value.to string. Since send value, our
one should be the exact same as the amount that we funded, we
can run just this one test, or running yarn, art hat, test,
dash dash grep. And we'll put this in quotes allow funded for
this amount funded line. And it looks like we ran into an issue
here, because we don't need to do deployed at addres
s, we can
just do deploy here. And great, it looks like we are indeed
passing. Now if we even run yarn Hardhead coverage will now see
we've got at least a little bit more coverage here. It's still
not going to be great. But we have much better coverage. We
have some statements, some branches, and at least some
functions covered. So this is awesome. Let's keep going. Are
we all done testing our fun function? Well, probably not.
What else can we do with our fun function? Well, we're also
adding fu
nders to a funders array. So let's go ahead and
test for that. So it adds funder to array of funders has been an
async function. And we'll say await fund mean that fund value
is going to be send value. We'll say const response equals await
fund me dot calling the funders array at index zero. So this
will be funder equals await fund me dot funders zero. And then
we'll say assert dot equal funder should be the same as the
Deployer. So let's go ahead and run this test. We'll hit up a
couple times.
And we'll change the GREP to under to array so
that it looks for this line. And perfect. It looks like that one
is also passing. Great. So the money's coming through, the
minimum amount is coming through and our data structures are
being updated. Awesome. Now we could be a little bit more
verbose and do even more testing with this fun function. But I
think for the most part, we've got the gist, right. So now
let's go ahead and move on to the withdrawal function. So
we're going to create a new de
scribe or withdraw. This is
going to be an async function. And let's see what the
withdrawal function does. Only the owner of the contract is
going to be able to get the balance, get the money back. And
we're also going to reset all of the amounts that each one of
these users is done. So let's go ahead and do some withdrawing.
Now in order for us to test withdraw, we probably first want
the contract to actually have some money and so what we can do
actually is we can add another before each in t
he describe to
automatically fund the contract before we run any tests. So we
can say before each async function. We can say await, fund
me dot fund. Value, send value. Now for all of our tests in this
withdrawal scope, we're first going to fund it with eath.
Let's say it can withdraw, withdraw eath. From a single
founder, this would be an async function. And this is going to
be a little bit longer test. So I'm going to set it up to be an
arrange, act and assert test. So arrange act assert is ju
st sort
of a way to think about writing tests, you want to arrange the
test, then you want to act. And then you want to run the
asserts, and you'll see what I mean in just a second. So we're
going to arrange this test, we're going to set this test up,
we want to actually check that we're correctly withdrawing the
ether from a single founder. So first, we're going to get the
starting balance of the fundraising contract and the
starting balance of the Deployer. So we'll say const.
Starting under m
e, balance, equals await, fund me dot
provider, dot get balance, fund me dot address. So we're gonna
start with the balance of the fund V contract after it's been
funded with some eath. And we're also gonna get costs start
starting, deploy your balance goes await, fund me dot
provider, dot get balance of deployed employer. So we're getting the starting
balance of the Funmi, we're getting to the starting balance
of the employer so that we can test later on how much these
numbers have changed base
d off of what happens when we call the
withdrawal function. Now that we've done a little bit of
setup, we can actually run this withdrawal function, we can do
the act here. So we're gonna say const, trans action response
equals await, fund me dot withdraw. And then we can say
const, transaction receipt equals await transaction
response. That Wait, one, and now we should be able to check
to see that the entire fund rebalance has been added to the
deployer balance. So now we can say const ending f
und me balance
equals a weight on me that provider dot get balance of
Unreal dot address. Then we can say const ending deployer
balance equals await, fund me dot provider dot get balance of Deployer. And now we can just
check to see if the numbers work out here. So we can say assert dot equal
ending fund me balance is going to be zero, right, because we
just withdrew all of the money. So ending funding balance should
be zero. And we'll say assert dot equal starting fund me
balance plus starting
deployer balance. So the starting funds
the balance plus the starting employer balance should equal
the ending employer balance. Since we're grabbing whatever
the starting deployer balance started with plus the starting
fund to be balanced, because we just withdrew all of the
starting fund, we balance that should equal the ending deployer
bots. Now a couple of notes here, since starting Funmi
balance is calling from the blockchain, it's going to be of
type a big number, we want to use big number
dot add actually,
instead of the plus sign here, just because it'll it'll make
working with our big numbers a little bit easier. So instead of
starting Funmi balance, plus we're gonna be starting from the
balance dot add. Like that. And that should be good. One other
thing about this, though, is that when we called withdraw our
Deployer did what our Deployer spent a little bit of gas. So
this actually isn't accurate. We actually also need to calculate
in the gas cost, so we wouldn't need to do
dot add gas cost.
We'd also have to do.to string because big numbers are objects
and so identities a little bit weird. So to test to see if
they're equal, we'll just make them both strings. Now we don't
have gas costs. So let's figure out how to get the gas cost from
this transaction. So we can add it to Are any deployer bounce,
so we can run this assertion here. So what we can do is we
can actually find the gas costs from our transaction receipt.
And I'm going to show you a couple of phenomenal
tricks you
can use with VS code. And if using a different editor, then
don't worry too much about this. What we can do in VS code
actually is create something called breakpoints. unverified
breakpoint file is modified to please restart the bug session,
or let's put it right here, put it right in this line after
transaction receipt is created. But before ending Funmi belts,
what this breakpoint does is it stops the script at this line,
and allows us to drop into something called a debug console
and see all the variables that are happening. At this time, we
want to look at the transaction receipt and see if the total gas
cost is in there. This is also incredibly helpful for dropping
into tests and dropping into scripts and seeing exactly
what's going on that's wrong. So what we can do is we can move
down to this run into bug section. And if it's not there,
you can hit additional views. And we can click this JavaScript
Debug Terminal, which will create a new terminal in our
terminal sect
ion. Now, what happens here is if we run yarn,
hard hat test, it'll run our testing and everything. But when
it hits this breakpoint, it'll stop. Currently, there is no gas
cost. So we're just going to delete this for now. So that we
compile and we work in everything. But if we run yarn
Hardhead test, see, it's gonna say debugger has been attached
to start running our tests. And it's going to stop on this line
here. And if we look in this variable section on the left
hand side, we can actually s
ee a ton of the variables that are in
here. And we can read a little bit more about what's going on.
And if we go over to our debug console, we can type in things
like transaction receipt, and we can see a ton of information
about that transaction receipt object, what we're looking for
is we're looking to see this transaction receipt, which we
could look in the debug console or over here, if there's
anything to do with gas in here. And it looks like there is
there's a gas used big number. And th
ere's also an effective
gas price. So the amount of gas used times the gas price is
going to give us all the money that we paid for gas here. So
now that we've figured out there's a gas used and effective
gas price variables in this transaction receipt, which we
could have also found in the documentation here. However,
sometimes it's even quicker just to find it out yourself what we
can do, we can exit the debugger by clicking this little thing
here. Go back to terminal will trash can the JavaSc
ript
debugger, we'll remove the breakpoint. And we'll grab those
two variables we can pull them right out of that transaction
receipt object by typing const. Yes, used comma effective gas
price equals transaction receipt. So again, with this
curly bracket syntax, we can use this to pull out objects out of
another object. And now that we have these two objects, we can
create a const gas cost or total gas cost is going to be equal to
the gas used times the effective gas price. Which again, since
t
hese are both big numbers, we can use a big number function
called dot mol to multiply them together. Now that we have this
total gas cost, we can come down and we can say the ending
deployer balance plus that gas cost to string. Now the two of
these should be equivalent. I know there's a lot of math that
we're doing in this section and a lot of new things. So I want
to just quickly rego over what we just learned. So first off,
the font of the contract comes with a provider, we could have
also d
one ethers dot provider dot get balance, but we're using
fun v dot provider because we're using the provider of the Funmi
contract. It doesn't really matter what we use here. We just
wanted to use this get balanced function of the provider object
which gets us the balance of any contract. We do the same thing
with starting deplore balance. The reason that we needed the
starting balances is because we wanted to compare it to the
ending balances to see if all the money went to the right
places, we
then call the withdrawal function. And from
the transaction receipt, we grabbed the gas used and the gas
price. If you want to debug your JavaScript code, you can add a
breakpoint like so go to run into bug, open your debug
JavaScript terminal, which is different from your regular bash
terminals. And when you run JavaScript commands in here,
they will stop where your breakpoints are. Then you can
read the different variables and see where different things are.
Using that knowledge. We pulled ou
t the gas use and effective
gas price from the transaction receipt and used it to get the
total gas cost of this transaction, we then got the
ending fund, we balanced the ending deployer balance, and
used all those variables to make sure all the money went to the
right places. And we, we can check this by running yarn
Hardhead test dash dash grep withdraw eath in quotes, since
there's a space here, and we can see that our test does indeed
pass. Great job. If we didn't add the gas cost here, and
we
just did.to string, we would see something like this, we would
see that the numbers are ever so slightly off, because we're not
anticipating we're not calculating the gas here. So we
always want to make sure we're using the gas if we're doing
calculations like this. Now another incredibly powerful
debugging tool that we're not really going to go over here.
But it's important to know about because it can be really helpful
is that you can actually use console dot log in your solidity
with hard
hat. If you're inside of a heart hat project, you just
import hard hat slash console dot soul. And then right in your
solidity, you can do console dot log, and then type pretty much
whatever you want. When you execute these functions, similar
to how we do a console dot log in JavaScript, those will
actually console dot log out to your terminal. Here's an example
of if you run yarn, hard hat test and you have those console
dot logs, you'll see stuff like this get printed out. So in
addition to th
e Visual Studio Code debugger, importing hard
hat slash console dot Sol, and using console dot logs in your
solidity can also be an effective debugging strategy.
Feel free to give this video a pause, implement this in some of
our contracts and try it out in our tests. So we tested that
withdrawing eath when there's a single funder works perfectly,
let's test withdrawing eath if there are multiple funders, so
we'll do it, let's say allows us to withdraw with multiple
funders. Why would this be an
async function. And let's do
this await Funmi not fun, but with a number of different
accounts. So we can create a whole bunch of different
accounts of course, by saying const accounts equals await
ethers dot get signers. And we can loop through these accounts
and have each one of these accounts call the fund function.
And we're going to do this with a for loop. So we're going to
say for let i equals we'll start with the first index of the
accounts because the zero with index is going to be the
Deployer. So we'll say let i equals one is going to be less
than let's say six. And we'll do i plus plus. And in here, we'll
say const. And me contract Funmi connected contract equals await.
And me dot connect two accounts, I, so we need to call this
connect function because right now if we scroll up back to the
top, our Funmi contract is connected to our Deployer
account. And anytime we call a transaction with Funmi, the
deployer is the account that is calling that transaction, we
need to crea
te new objects to connect to all of these
different accounts. So we're gonna say fund me connected
contract, which is now connected to one of these different
accounts dot fund. And this is where we'll do value, send
value. Or excuse me, we'll do await. Great. So this is going
to be our our range section. And then same as we did above, we
need to grab those starting balances. So we can just copy
that, those two lines and paste that down here. Now we're going
to move into act, I'm going to call th
at withdrawal function
again. So let's say const, transaction action response
equals await and MI dot withdraw. And we're going to do
the exact same thing as we did above, getting the transaction
receipt and the gas costs so we can get everything correct. Once
we've done the act, we move on into assert. And we're going to
do some very similar things to what we did above, like this,
for example, this whole first part is going to be exactly the
same. We also want to make sure the funders are reset
properly.
So we'll make sure that this funders array is reset properly.
So to do that, we can actually just check to see that if
looking at the zero with position throws an error so we
can run a wait expect fund me dot get on me dot funder on me
dot funders of zero. This should revert so we'll say await expect
Funmi dot funders dot two dot b dot reverted and then we want to
loop through all these accounts and make sure that it makes sure
or that in our mapping here, all their amounts are zero.
So we'll
say, for I equals one is less than six, i plus plus, we'll say assert
dot equal. Wait, fund me dot address to amount funded, of the
accounts of I got address should be zero. So we're making sure
that all of these mappings are correctly updated to zero. So
let's go and test this. So we're withdrawing with multiple
founders, we're going to go back to our terminal, we're going to
hit up, we're going to change this GREP or this one. We'll see
if this passes. And it does indeed, so this mean
s that our
withdraw function works really well even when there's multiple
funders and we can be happy and go to sleep knowing that. Now
the other thing we absolutely want 100% want to test is that
our only owner modifier is working, we want only the owner
to be able to withdraw the funds from here. So we'll create a new
session, we'll say it only allows the owner to withdraw
only allows you to withdraw. This will be an async function.
And in here, we'll say const. Accounts equals ethers dot get
signers again. And we'll say const attacker equals accounts
of one. So we'll say the first account will be some random
attacker will connect this attacker to a new contract,
we'll say const attacker connected contract equals await
on DB dot Connect. Attacker dot address, excuse me a dot connect
attacker. Since we're not just connecting the address, we're
connecting the account which attacker is an account object.
And then we'll do a wait expect attacker connected contract dot
withdraw dot two do
t b dot reverted, they should not be
able to withdraw. So let's go ahead, we can even just copy
this whole thing if we want to hit up, we'll delete this
section here. We'll paste that in. And boom. This means that
when some other account tries to call withdraw, automatically
gets reverted, which is what we want. Now we can be more
explicit to make sure that the correct error code is being
thrown, not just that it's reverted. Right, it can be very
reverted because they sent ether did it they did
something weird,
we want to make sure it's reverted with our specific error
code. So right now we have this not owner error code. But it's
actually a best practice to put the contract name to underscores
and then your custom error. This makes it a lot easier in the
future when you have a ton of different contracts, and you're
not sure where an error is coming from. So we're going to
just update this really quickly to be fund me underscore
underscore, not owner, now we can do is now that we have
this
custom error, we can say withdraw that to be reverted
with, then we can add our custom error in here. Now, if we rerun
our test, with only allows the owner to withdraw, oops, we need
to do a wait here. My mistake, wait a Thursday, get signers.
And now let's try this again. And we are indeed passing
Perfect. Okay, great. We have some basic
unit tests here. And we're going to write some staging tests
pretty soon. But before we actually do that, let's go ahead
and add the gas estimator. And we
'll see how much gas these
contracts in these functions are taking. It looks like the
Hardhead gas reporter is already here. So let's scroll down.
We'll do gas reporter true. And we won't do coin market cap
here. And we'll just look purely at the GUI. So you can just
comment it out like that. Now rerun all of our tests. So we'll
say yarn, our test. And in doing so we're gonna get that that gas
output in that gas report dash text here. So looks like all of
our tests are passing, which is perfect.
Now we can look into
our gas report and see what's going on here. i Well, it looks
like the fundraising function is taking a decent chunk of gas,
the withdrawal function, take it some guests to we'd see the min,
the max and the average. Of course, we can see how much each
one of these contracts cost to actually output. We don't really
care about the MOQ aggregator of course, because we're never
actually going to use that. Let's say we look at the average
gas for these and we go hmm, this looks
like it's actually a
lot more than what we originally expected. Is there a way for us
to make this a little bit cheap. If we go back to our funding
contract, we look at our withdrawal function. And we
noticed something, oh, there is actually a way to make this a
lot cheaper. And it has to do with something called storage
variables, or these global variables that we've been
working with this whole time. Let me let me paint you a little
picture here, we're gonna look at one of the first gas
optimi
zation techniques you can take to drop these down. And it
has to do with an R Funmi. Contract these state variables
and how they're actually stored and how this contract actually
keeps track of all this stuff, this section is going to be a
little bit more advanced. So we'll have a note here saying
that this is an advanced section, if you want to skip
over it, you can, because now we're getting into gas
optimizations here, this information still is really good
to know. So if you want to skip it f
or now, and then come back
later, you absolutely can. But let's talk about what happens
when we actually save or store these global variables. Okay,
these storage variables. Now, everything I'm about to go
through is in the documentation. And there is a link to this, of
course, in the GitHub repo associated with this course,
whenever we have one of these global variables, or these
variables that stay permanently, they're stuck in something
called storage, you can think of storage as a big giant
array, or
a giant list of all the variables that we actually
create. So when we say we have some contract called Son of
storage, and we have a variable called favorite number, we're
basically saying we want this favorite number variable to
persist, right, we saw in a lot of our examples, we had a
favorite number variable that we can always call to see what this
contracts favorite number was, well, the way it persists, is it
gets stored in this place called storage. A storage box is this
giant li
st associated with this contract where every single
variable and every single value in the storage section is
slotted into a 32 byte long slot in this storage array. So for
example, the number 25 in its bytes implementation is 000 with
a ton of zeros 19. This is the hex version of the yuan 256.
This is why we do so much hex translation, the bytes
implementation of a yuan 256. And each store saw increments
just like an array starting from zero. So for example, our next
global variable or next sto
rage variable just gets slotted at
the next slot that's available. So bullions, for example, get
transformed from their bull version two, their hex and we
modified our some bool variable to be true and X edition of the
true Boolean 0x 001. Every time you save an additional global
variable, or more correctly, one of these storage variables, it
takes up an additional storage slot. And what about variables
that are dynamic in length, or that can change length? What
about something that's dynamic? W
ell, for dynamic values, like a
dynamic array, or a mapping elements inside the array or
inside the mapping are actually stored using some type of
hashing function. And you can see those specific functions in
the documentation, the object itself does take up a storage
slot, but it's not going to be the entire array. For example,
my array variable here at storage, slot two doesn't have
the entire array in storage slot two, what has actually is just
the array length, the length of the array is sto
red at storage
slot two. But for example, if we do my array dot push 222, we do
some hashing function, which again, you can see in the
documentation what that is, and we'll store the number 222. At
that location in storage, the hex of 222 is 0x 0000 D, so it
gets stored in this crazy spot. And this is good, this is
intentional, because 32 Bytes may not be nearly big enough to
store my array if our array gets massive. And it wouldn't make
sense for to put the elements inside the array at subseque
nt
numbers because again, the size of the array can change and
you're never going to be sure how many subsequence that you
need. So for my array, it does have a storage slot for the
length for mappings. It does have a storage spot as well
similar to array, but it's just blank. But it's blank
intentionally so that solidity knows, okay, there is a mapping
here, and it needs a storage slot for attaching functional
work correctly. Now interestingly, constant
variables and immutable variables do not
take up spots
in storage. The reason for this is because constant variables
are actually part of the contracts byte code itself,
which sounds a little bit weird. But you can imagine what
solidity does is anytime it sees constant variables name is it
just automatically swaps it out with whatever number it actually
is. So you can kind of think of not in storage is just a pointer
to 123 and it doesn't take up a storage slot. Well when we have
variables inside of a function, those variables only exi
st for
the duration of the function. They don't stay inside the
contract. They don't persist they're not permanent. So
variables inside these functions like new var and other var do
not get added to storage. They get added in their own memory
data structure. Which gets deleted after the function has
finished running. Now you might be asking, Okay, well, why do we
need this memory keyword, especially when it comes to
strings, we saw before that we had to say String memory. The
reason we need it f
or strings is because strings are technically
this dynamically sized array. And we need to tell solidity,
hey, we're going to do this on the storage location, or we're
going to do it into the memory location where we can just wipe
it. arrays and mappings can take up a lot more space. So slowly,
just wants to make sure Okay, where are we working with this
is it storage is a memory, you have to tell me, I need to know
if I need to allocate space for it in our storage data
structure. And again, eve
rything here you can read in the
solidity documentation. Now, in the GitHub repo associated with
this course, if you go to contracts, we've actually got an
example contract section called Fun with storage, where you can
play with and look at a lot of this stuff. And we even wrote a
little script called deploy storage fun, where it'll print
out the storage location of some of the different variables, feel
free to give it a run, if you want to try a challenge anybody
to write some functions that f
ind the storage slots of the
elements of the arrays, and the mappings, and then find the data
inside of those as well. We use a function here called Get
storage app, which allows us to get the storage at any one of
these slots. And this is to reinforce that even if you have
a function as private or internal. Anybody can still read
it. Anybody can read anything off the blockchain. And you can
test it exactly what this if you go ahead and get clone that or
copy paste the code yourself. You can the
n run yarn part at
deploy dash test tags, storage. And you'll run the deploy script
for that storage. And you'll see printing out the
location of storage in each storage slot with a fun contract
that we made as an example. And you might of course be asking,
Okay, Patrick, why are you telling me all this? We're just
trying to get this gas price down? Why are you telling me all
about this storage thing? Well, the reason I'm telling you all
about the storage thing. Anytime you read or you write to
and
from storage, you spend a ton of gas. Remember I said when we
compile our code, we compile it down to some crazy weird
bytecode. Well, let me show you on remix what this looks like.
We go to compliation details, we can go to bytecode. And we see
this weird hex object zero, blah, blah, blah. But we also
see these things called op codes. Now, this bytecode here
represents these op codes. Each one of these op codes represents
a small piece of everything in this bytecode. And in fact, in
our hea
rt hat, we can go to artifacts, build info, and we
can see, we can see these op codes in the build info, we can
do a command F or Control F for opcodes. We can see op codes for
different contracts. These op codes represent what the machine
code is doing. And they represent how much computational
work it takes to actually run our code and do stuff with our
code. The way that gas is actually calculated is by these
opcodes. There are a couple of lists here. But here's one that
I'm going to use this
EVM opcodes. And again, there's a
link to this in the GitHub repo associated with this lesson.
Well, if we scroll down, we can see exactly how much it costs
for each one of these op codes. So for example, anytime we add,
it costs three gas, anytime we multiply, that's five gas,
subtracting three gas, we have all of these op codes that cost
different amounts of gas in in our functions, here's kind of a
sample contract. If we're doing adding, anytime we add it's
going to cost three gas. Anytime w
e save to memory, it's going to
cost gas from some other opcodes. These op codes combined
show us how much gas we actually use. Now, let's look at a lot of
these op codes and how much they cost three 510, three, three,
balance is 700. So getting the balance is is a ton of gas.
Let's keep going. Getting the sides of an Accounts code is a
lot of gas copying and accounts code into memory. But oh my
goodness, what is this save word to storage costs a ton of gas
that is 20,000 gas and s load load wor
d from storage cost 800
gas. These are two of the most important opcodes s load and s
store which stands for storage load and storage store. Anytime
one of these op codes fires, we're spending 800 or 20,000.
You know, there's a big asterisk there because that can change a
lot. But we're spending a ton of gas anytime we work with storage
as developers. Anytime we work with some stuff in storage, we
want to go boy, this is about to cost me a lot of gas and the
best convention or making sure we kno
w that we're working with
a stored variable and we're about to spend a lot of gas is
to append an S underscore right before them which stands for
storage right So we're saying address to amount funded is
going to be a storage variable wonders is going to be a storage
variable. Owner is not going to be a storage variable. It's
immutable. The best practice for immutable variables is
prefixing. It with an I underscore constant values are
also not in storage. So for constant values, we want to keep
them capslock, like that aggregator v3 interface public
price feed. Yep, you know what that is going to be a storage
variable. So we want to append an S underscore with it. So
we're going to do a little bit more refactor, we've appended
these appropriately to update everything. So instead of owner,
it's going to be I underscore owner, and as a developer will
read this, and we'll go ah, this is going to be much cheaper than
a regular variable. Okay, that's great. I'm going to work with
this. This
I underscore owner for my modifier. Awesome. Is
owner anywhere else in here? Okay, right in the constructor.
I own underscore owner is message dot sender. Price feed
is a storage variable. We should as developers, we should see the
s underscore when reading this and go, Okay, we're spending a
lot of gas to store this. Perfect. Okay, great. Let's keep
going. Great. We've updated all the owners. Okay, well, what
about address to amount funded? In VS code? If you do Command F,
or Ctrl, F, and you
hit this little down arrow, you can
actually find and replace all of these address to amount funded
with s underscore address to Mt funded. Hit it like that. And
since I updated one, I got a backspace that one no. So now
these are all updated. Let's do the same thing with s funders.
Let's update everywhere. It has funders just to be s funders.
And we probably doubled up here. Yep. Let's undo that. We already
updated all the AI owners. So now it's updated all the price
feeds. So let's look for pr
ice feed, we'll update it with ES
price feed. And then we probably doubled up right here. So we'll
undo that. Okay, great. Now that we've updated everything in
here, we can scroll down and we can oops, sorry, doubled up
there too. Sorry. We can we can green through our code and go
okay, where are we reading and writing to storage way more
often than we probably need to. And that's when we get to this
withdrawal function, which seems rather suspect to reading and
writing to storage a lot. So let'
s take a look at what we're
doing here. Okay, so first of all, I can see that we're doing
a for loop here. And every time we do a for loop, we're just
constantly looping through all of this code. Every single time
we're doing a little compare option here we're saying, okay,
is our funder index less than s funders dot length? S funders
dot length, this means the longer our funders array is, the
more times we're going to be reading from storage, that's
incredibly expensive. We're also recalling th
is, oh my goodness,
we're reading from storage a lot and saving it to this memory
variable, and then updating our storage variable with it. Wow.
So we're reading from storage a ton here, and we're reading from
storage a ton here, okay, then we have to reset our funders
array, there's really no way around it. And that's pretty
much it for our reading and writing to storage, we could
probably create a withdrawal function, that's a lot cheaper.
So let's go ahead and create a function called cheaper
withdraw, function, cheaper withdraw. That's going to take
what we've just learned and make a cheaper withdraw, that's much
more gas efficient. So we'll keep this public payable. and
have it be only owner, we're not going to change anything there.
But what can we do for at least this part here, we don't want to
keep reading from storage here. And we don't want to always have
to keep reading from storage here. We're like doubling up the
amount of storage we're reading from. So instead, what we c
an
do, we can read this entire array into memory one time, and
then read from memory instead of constantly reading from storage.
And that's going to make our lives a lot cheaper. So we can
create an address array. Memory funders equals s underscore,
funders. And now it's going to start making sense why for
arrays and strings in our functions, it makes us say, hey,
is this memory is a storage? What is this? And we're telling
it, we want it to be memory because memory is going to be a
lot cheaper.
So now that we're saving it into our funders, oh,
and a quick note mappings can't be in memory. Sorry. They're
just too weird and too wacky. So flooding just doesn't let you do
that right now. But now that we've saved our storage variable
into a memory variable, we can read and write from this memory
variable, much, much cheaper, and then update storage when
we're all done. So what we're going to do now is we're gonna
say for you into V six, fund or index equals zero, and we're
going to basical
ly rewrite everything but just using this
memory array. instead. We're gonna say funder index is less
than funders dot length and instead of s funders dot length,
and then we're gonna say funder index plus plus. And then in
here we're gonna do nearly exactly the same thing, except
we're gonna say address funder equals funders, using our memory
array and not s funders have funder index. And then we're
gonna say s address to amount funded. funder equals zero. So
we're resetting our funders mapping
here, we're using our
memory variables instead, then we're going to do the same thing
s underscore funders equals new address array of zero. And then
we're gonna do the same thing bool success, comma equals S
owner, dot call value, address this balance, and we're
going to send it nothing and then require success. Like that
actually, sorry, Iona on a sonar now that we have this function
that we think is cheaper, let's go back to our test. And let's
run this same multi test here, but with our che
aper function.
So I know this can be a little bit tricky to copy, paste, but
let's copy this entire massive test. Let's come down here,
paste it, and we'll change the name saying cheaper withdraw,
testing dot that done. And in here in in this giant it here,
all we're going to change is we're going to change withdraw
to cheaper withdraw, and the rest of the test is going to be
exactly the same. So with that, let's see if if we were
successful in making our withdraw function cheaper with
cheaper w
ithdraw, we're gonna pull apart our terminal now. And
we do yarn, art hat test, which is going to run our gas
estimator because it's enabled right now. And of course, all of
our functions have been broken because we renamed everything.
So we'll do a quick final replace of funders, to change
funders to ask funders. And then we're gonna change price feed to
ask price feed. And then do we have owner anywhere? We don't
have owner or anywhere. We need to change this one. Address to
amount funded. Let
's come in here. Address to amount funded.
S adderstone. well funded. What else do we need to change price?
We price feed funders. Okay, I think we changed everything.
Alright, so let's try our test now. Yarn Hardhead test. All
right, great. Everything's passing. And we ran our cheaper
withdraw testing. So now, if we go to our gas output here, our
gas report, zoom out just a hair, we can see the difference
between cheaper withdraw and withdraw, we see something
really interesting. Here we see ou
r cheaper withdraw on average
was actually more expensive than our regular withdraw. And the
reason for this is because actually, if we go to our tests
are cheaper withdrawal, we only tested on the multi withdraw. So
we had to reset many, many more accounts. But this was also
technically its maximum as well. And if we compare the maximum of
the cheaper withdrawal, the maximum of the withdraw, it
looks like the cheaper withdrawal was indeed cheaper.
And if we go to our hard hat dot config, and we
add our API key
back in, what we could even do is in our test, we could copy
withdraw eath from a single funder, copy that, paste it in
and just change withdraw to cheaper withdrawal, rerun it
with the key and now do yarn Hardhead test, we can see
exactly how many dollars we would save if we ran this on the
Matic blockchain. Now let's go back, we'll reopen up our gas
report. And we can see, in the minimum cheaper withdrawal was
actually a little bit more expensive. This actually does
make sense
because if we look at funding, if we only have to
withdraw when there was one funder, well this loop only runs
one time. And our cheaper withdraw will do the exact same
but it will have this this extra thing here of loading them all
in, we see that the savings The more people our funders in our
contracts. So automatic, we can see we pretty much didn't save
anything. But if I change this one more time to eath run the
test again. Now we can see people withdraw saved a few
cents. This is how we ca
n start optimizing our contracts will be
cheaper and cheaper. And this two cents was just in the
average. It's not even comparing the max to the max, which was a
lot more gas than their averages. We have just learned
an absolute ton here. Now, this next part is going to make some
of you mad because we're going to refactor our code one more
time if you don't want to refactor it and you want to
leave all your tests as s underscores you absolutely can
but to other users using our application. Deali
ng with this s
underscore is a little Little bit gross and actually can make
our code a little bit more confusing for those who use it.
And additionally, right now all of our state variables are
public. And actually internal variables and private variables
are also cheaper gas wise. And we don't need to make every
single one of our variables public, because anybody can read
them off the chain anyways. So one more refactoring that we're
going to do is we're actually going to set the visibility of
these to private or internal, based off of whether or not they
need to be private or internal, and then we'll create getters at
the bottom of our function here. So minimum USD, we can keep this
public because we want other people to know what the minimum
USD of our contract is, without having to go right through
storage, the owner of our contract isn't important for
others to know or other contracts to know. So we can go
ahead and make this private, and then at the bottom at a function get owne
r. That's a public view
that returns I owner, turns address as funders, as funders
can be private as well. So at the bottom, we're gonna say
function, get funder, and we're going to pass a un 236 index,
public U, turns, address, return as funders of index, the address
to Mt funded can also be private. So the bottom, we're
going to create function, get address to amount funded. And
this is going to take an address under the public view returns,
you went to 56. And we're going to return amount fun
ded of the
funder did this one we did this one, we did this one. And then
price feed, function get price feed. This is going to be public
view as well, that returns aggregate tour of the three
interface that's going to return so I'm just gonna price feed.
Oh, okay. The reason why we did that is because we want to have
this s underscore so that we as developers can know, okay, this
is a storage variable, I want to be very careful about how I
interact with this. But we don't want people who intera
ct with
our code to have to deal with this s stuff. And we want to
give them an API that makes sense, and that it's easy and
readable. So we add these getter functions at the bottom to do
that. And also changing the visibility can save us some gas
in the long run as well, because we're gonna be calling from
private variables or internal variables, which are cheaper gas
wise, of course, we do need to upgrade our test one more time.
And like I said, if you want to just leave them with the S
unders
cores, that's absolutely fine. So s underscore price feed
is now going to be replaced with get price feed, s underscore
amount to fund it is going to be now replaced with get address to
amount funded. For now going to change SW, underscore funders,
to get funder. We're going to change Iona, there's nowhere
else in their mind, we don't need to change them. And I think
that was everything. Let's just look for s underscore, we don't
see it, I underscore, we don't see that either. Let's just run
our
tests one more time to make sure we refactor that correctly.
And it looks like we did awesome. Okay, we have just
learned a ton. we've refactored our code a ton. And everything
is starting to look really, really good here. One more gas
optimization we could make. And an optimization for errors as
well is we could update all of our requires to instead be to
instead be reverts. Because without requires, we're actually
storing this massive string, this massive array of text on
chain, these error c
odes are much cheaper. But that's
optional. If you want to do that. The whole reason we were
doing this is we were going through the style guide in
updating things here. So we have public internal private, and the
bottom is going to be our view slash pure functions, which they
are they're just all these getters that we just added. So now our style in here looks
good. We've learned a lot about gas, we've learned a lot about
storage. This is fantastic. Let's do a quick refresher on
everything we j
ust learned because we went through a lot
right there. And like I said, this is one of the harder parts
of this course. Any variable that is changeable that we want
to persist across contract executions and transactions. We
save to a giant array called storage this array is
sequentially indexed starting at zero. So the first variable the
first value that we have in our contract gets stored to the zero
with index. The next one gets stored to one and so on and so
forth. Dynamic arrays and mappings
and other dynamically
sized objects use specific hashing function that you can
find in the documentation to determine where the elements of
those dynamic Data Structures go memory variables, constant
variables and immutable variables don't go in storage.
And one of the main reasons talking about storage is so
important is because the op codes for loading from storage
and for reading from storage and writing to storage are
incredibly gas expensive. So in everywhere we can we want to
reduce the a
mount that we read and load from storage. And it's
one of the easiest ways to save gas and try to optimize our code
to be gas efficient. Like I said, some of this gas stuff can
be a little tricky and a little bit confusing. So if you don't
get this right away, it's okay. It is totally fine. If you're a
little bit confused, and you're like, What is he talking about?
Like I said, this is some of the more advanced stuff it'll come,
the more you work with solidity, and the more you work with
everyth
ing here. So don't let it stress you out. Don't let it
stop you from continuing. You're doing fantastic being here. So
far. We've written some really good unit tests. Let's now write
some staging tests. And these are the tests that we can use on
an actual test net. This is a test that we're basically going
to run after we've deployed some code just to see if everything
is working approximately the way we want it to. So let's go ahead
and we'll create a new file here. We'll call it fund me. Dot
s
taging dot test, dot j s. And it's going to look really
similar to what we were just doing with our unit tests. And
we're going to assume this is on a test net. So these are tests
that we're going to run, right before we deploy this to a main
net, this is the last step in your development journey, we
want to just make sure that everything is working
approximately correctly on an actual test net. So what we're
gonna do is we're gonna do the same thing describe, fund me,
and I'm gonna go a little
quick through these tests here,
because we've basically written this type of test before, so
we're gonna say before each, it's gonna be an async.
function. We're gonna do the same thing as our unit tests. So
we're gonna have a Funmi variable, we're gonna have our
let Deployer, we're gonna have our constant send value equals
ethers.utils.rs ether of one, and in here, we're going to do
const. Get named accounts. Equals require hardhats, we're
going to say deployer equals weight, get named accounts
,
we're going to wrap this up, dot Deployer. We're gonna say fun me
equals await ethers dot get contract. And me, comma, we're going to
connect it to our Deployer, we're not going to deploy this,
we're not going to do any fixtures like we did in our unit
tests, because in our staging tests, we're assuming that it's
already deployed here. And we also don't need a mock because
on a staging, we're assuming that we're on a test net, now we
can actually wrap this whole thing to make sure that we're o
n
a test net by using our helper config. And looking for our
development chains, we can say we only want to run our describe
bit if we're on a development chain. So first, we'll say
const. Development chains equals require, we'll pull that that
helper config in. And we'll say, development chains dot includes
network dot name. And we'll basically will say if developer
chains that includes network dot name, we're going to skip and we
can actually skip using this, we're going to use something
calle
d a ternary operator is basically like a one liner if
statement. And you can think of this as a special type of F.
I've got a link to this and the GitHub repo associated with this
course. And here's some JavaScript documentation,
showing it in action. You say, Okay, return is member. And if
it's true, have it be $2. Otherwise have a B $10. And
that's pretty much it. So another way of thinking about is
like, you say, let variable equals true, then we could say,
let some var equals variable questi
on. Yes? Or no? Some var
will end up being Yes. Because variable is true. If variable
was false than some var would be no. So it's literally saying, If
variable, if variable, then some var equals yes. Else. Some var
equals no, these lines are literally the exact same thing.
This one is just a little bit more succinct. That's really it.
So that's what this operator does. We're gonna say
development chains that includes network dot name. So if our
network is a development chain, which we're going
to import
network as well from hard hat and ethers as well. Then we're
going to do describe that skipped and this tells our test
to just skip this whole describe, and then we're going
to put this little colon here thing and save and boom. So now
we're only going to run this if we're not On a development
chain, and we want to take this exact same syntax, we'll go to
our unit testing here, and we'll do the exact same thing, we'll
paste it will have this be the opposite by putting a little
knot her
e, sticking that colon in. So now, our unit tests only
run on development chains in our staging tests, only run on test
nets. Perfect. That's what we want, allows people to fund and
own and withdraw. And this will be an async function, of course,
and we probably can make this pretty robust, but we'll just
say await, fund me dot fund is going to be send a value. And
then we'll say a weight on me dot withdraw. CERT equals
require try. Now we'll do kind of a lame final one, we'll say
const ending b
alance equals a weight on me dot provider dot
get balance, fund me dot address. And then we'll say
assert dot equals ending balance.to string, comma zero as
a string, we're only going to run this on a test net, I'm just
going to give you this one more run to show you it in action.
Feel free to skip this part again, because we are going to
be working with a test net, I'm going to run yarn, hard hat
deploy dash test network Rinkeby. And it's going to run
through our deploy. And after it's all depl
oyed, we're going
to run our staging tests to make sure that everything works even
with a price feed on a real test net, and I need to do const
development chains equals require dot dot slash dot dot
slash helper heart config. Now for run yarn, hard hat test,
we'll see just our unit tests get run. But if we run yarn hard
to test dash dash network Rinkeby, we're not going to run
nine tests, we're only going to run our singular staging test.
And of course, this is going to be a lot slower, because
we're
on a test net. Net will we write
written all these tests, we can write a couple of scripts. And
then we're going to finish this out by pushing this up to GitHub
making this our first smart contract GitHub repository, when
it comes to the blockchain, when it comes to smart contracts,
interacting with community interacting with open source
being a part of GitHub, or git labs or whatever Git hosting
service you're using is essential to being successful
here. So let's write our scripts. And t
hen we'll upload
this to GitHub to start building our portfolio. So first, we're
going to create a script to interact with our code called
fund dot j s. And this is going to be really similar to our
tests. And this way, in the future. If we want to just fund
one of our contracts very quickly, we can just run this
and we can do it, we're gonna do the same thing that we've been
doing, we're gonna do an async function main. And down below,
I'm just going to copy paste this because we're going to be
copy pasting in a lot. We're going to paste this little
syntax here. So let's write a script that allows us to fund
our contracts. So first, we're going to need const get named
accounts just like in our tests, equals require arhat we're gonna
say const Deployer. Equals await yet named accounts, just like
that. And then we're gonna say const fund me equals await
ethers dot get contract. From funding comm a Deployer.
Literally, almost exactly the same as our tests, then we'll do
a little console
dot log funding contract that that and we'll do
const transaction response equals await funding dot fund.
And for the value, we'll do something like ethers dot utils,
that parse ether of 0.1 or something, whatever you want to
do here, we of course, need to import ethers, which it looks
like we already have. We'll do await transaction response dot
wait for one transaction. And then we'll do console dot log
funded, we can run this little script by running yarn, RT head
node will run a local node w
ith all of our contracts deployed.
We'll see if our script looks okay by running yarn, hard hat
run scripts fund dot j s dash dash network localhost. And it
looks like it's funding Great. Let's now write a withdraw
script withdraw that J S. And we can even leave our localhost
Node running because we're going to withdraw the funds that we've
just funded it with. So we're going to do the exact same setup
here. We can even copy this main bit to our withdraw Ah, the top
will do async function main w
ill say const deployer equals await,
get named accounts, which wow, I hit enter and my VS code auto
imported it, that's pretty nice. Maybe yours will too, maybe
won't if it won't, you just gotta write it out or copy paste
from the other one. And then we'll do cars Funmi equals await
ethers dot get contract on me, course. And this is going to be
the exact same. Now we're going to do console dot log funding
data dot, we'll say const transaction response equals
await on v dot withdraw, await transa
ction response dot wait
one, then console dot log. Got it? Back. And we can test this
out by running yarn, hard hit run scripts, withdraw dash dash
network localhost, we'll see if this works. And cool and our
script is working. Fantastic crushed out two scripts
incredibly quickly. And now we have a way to easily interact
with our code with our contracts if we want to via a script.
There's actually one more thing I want to show you before we
actually work and we push all this wonderful code up to
GitHub
in our package dot JSON. I've shown you a little bit of this
before, but we can add this scripts section to make our
lives a lot easier and condense all these long tests into a yarn
script for us. So usually, what you'll see in common package dot
JSON is you'll see a list of these in here for people to look
and just automatically run, one of the most common ones is going
to be test. And to run test, we're going to do yarn, hard hat
test. So now instead of running yarn, Hardhead test, som
eone can
just come to your package once this is saved and just run yarn
test. And this will grab this test from your script section
and it will run yarn heart at test and Bada bing, bada boom, okay, cool. What else do we
probably probably want to do in here? Well, we probably want to
have a test staging section that'll run yarn Hardhead test
dash dash, network Rinkeby. I'm not going to run that. But
that's probably something we want to have in here, we're
probably going to want some linting. So
we showed you
briefly that linting thing. So we'll have a yarn lint, which
will just run yarn, sole hint. And then we'll just have it so
hence the contracts folder. And anything that starts out soul.
So now if I run yarn, lint, it'll run soul hint, and all of
our code here and it'll give us some warnings here, which we can
pretty much all ignore. And Solon actually has an auto fix
and auto fix that we can add, by doing yarn lint fix, we'll say
yarn. So hint, contracts slash start out. So we'll d
o dash dash
fix. So now we run yarn, lint fix, it'll auto fix, which
there's nothing to auto fix. So nothing happens. But it's good
to have anyways. And then we can do our formatter, we're just
going to format our code format, that which will do yarn,
prettier, dash dash write to, and we'll just do a period to do
everything. And then we can just do yarn format. And it's gonna
fix all of our, it's going to fix everything for us, which is
great. And then we finally can have coverage. By running ya
rn
hardhat coverage, now we can just run yarn coverage, will
give us this wonderful little coverage report. Awesome. So our
packages looking fantastic. Maybe we'll even come into
package json. We'll give this a name. We'll call this hard hat.
And me give it an author, which is going to be your name. So I'm
just gonna say Patrick Collins, and we'll give it a version in
here. of 1.0 Point Oh, oops. And let's do icons instead of
spaces. Oh, and one more thing. We're not using ES lint. So all
this e
s lint stuff, we can dump and we could delete the yarn dot
lock and reinstall. But yeah, whatever. We don't have a
readme, but that's okay. If you want to go back, like I said, go
check out that best readme template and go update your
READMEs to make them look as awesome as this, you can do that
as well. But other than that, we've got an awesome code repo
here. What do we want to do with it? Well, we've been playing
around with GitHub so much, we've been looking at all these
good hubs. It's time
for us to join GitHub with our own with
our first repository. Let's go ahead and let's make this happen
in the lesson seven full blockchain solidity course, yes,
there is a link to this GitHub Quickstart that we're going to
follow to set up our first repository. This is going to be
the moment where you are starting to build your portfolio
building and GitHub is borderline crucial for your
development journey. It's going to be your portal folio, it's
going to be where you say, Hey, look at all t
he cool products
that I'm engaging with, that I'm working with, that I'm
participating in. If you've already made GitHub before, I
highly recommend you still push this up to GitHub as proof that
you've done it. And then you can also tweet it at me saying, hey,
look how far I've gotten. Look where I've done, look at how
fantastic I'm learning smart contracts, and be incredibly
excited about now, this quickstart will walk you through
creating a repository, creating a branch and teaching all this
s
tuff, we're going to follow the instructions from the GitHub
documentation about adding locally hosted code to GitHub,
we already have a project, and we're just pushing it up. Since
the Windows users are using WsL, you can just follow the Mac or
Linux instructions here, the first thing that we're going to
do is in your GitHub profile, or your GitHub login, we're gonna
hit this little plus thing, and hit new repository, you can call
this whatever you want, let's call it hard hat, fund me b code
c
amp, you can put a description if you want, learning from
recode camp, and Patrick, about smart contracts. We'll make it public because we
want other people to see you being fantastic and learning
smart contracts. We'll leave this blank, and we'll hit Create
repository. Now, this is our public code repository. This is
our first one, if you've done these already, this will be your
first smart contract one. It even has some instructions in
here, too, that teaches us how to create a new repository
from
the command line, you can follow this if you want, or you can
follow like so the first thing we're going to do is we're going
to initialize a git branch. And from way back when you should
already have Git installed. Remember, you can check by
running git dash dash version like this. Git is a little
different from GitHub, Git is known as version control. And it
allows us to make changes to our code, but keep a history of all
the code changes that we've made. GitHub is a place where we
can pu
sh all of these changes and keep track of all of our
code. So the first thing that we're going to do is we're going
to create a new branch. And I'm not going to explain Git and
working with Git too, too deeply. But if you want to learn
about Git Free Code Camp, of course, has a video on Git and
GitHub for beginners. So we're going to create this new branch
with git init dash b main, now your Visual Studio code might
automatically start formatting some stuff. And if it does,
that's great. And if
it doesn't, don't worry about it. But what
you'll see here on the left is you'll see some stuff is green,
and some stuff is gray, open back up that dot Git ignore,
you'll see that all the gray stuff is the stuff that we have
in this dot Git ignore this is intentional, this is what we
want. This is just some some highlighting saying, hey, this
green stuff is what you're working with on GitHub. And this
gray stuff is what you're not working with and GitHub. And
that's what we want. You don't need
to push artifacts, or cash
or coverage or node modules, people can install and compile
on their own machines, we definitely don't want to push up
our Dotty and V if we're using a Dotty and V. And we don't really
need to push up coverage dot JSON, either the rest of this we
do. So now what's next, after we initialize our main branch, you
now should be able to run git status in your terminal and see
this huge red output of all these things that are untracked,
and we don't have any commits for, wha
t we can do now is we
can stage all of our code with git add period, and then commit
it with Git commit, we're going to run these one at a time
instead of together. Before I run git add, I'm going to add
deployments to this list too, because GitHub doesn't really
need to know about all the different deployments I make,
especially when I make a ton of deployments to my local hosts,
they don't need to know. So we're going to add that to, then
we're going to run git add dot. And if we do a git stat
us, now,
we can see all of our code has been staged for being committed
for being a part of this history of our code. And then take a
quick look at all these files and make sure your dot E and V
file is not and never in here. So now we're going to run git
commit dash m and then this message for our commit, okay, so
we'll run git commit dash m, we can say, initial commit, or
whatever you want in this message here, first commit,
initial commit, who cares, and it's gonna say, create mode,
blah, bla
h, blah, for all of these files. And if we do git
status, now, it's blank on branch main, nothing to commit.
And then you might get something like this. If you've never
worked with Git before your name and email were added
automatically. We're a little bit confused here. We'll talk
about this in just a second. So next, what we can do is we're
actually going to copy the URL of our GitHub repo. So you can
grab that just by copying here or right at the top, that URL
right there. And what we're goin
g to do is we're going to
add this as a remote repo. To do that. We'll do git remote add,
and we'll give this remote repo a name. For us. We'll say origin
and then we'll paste that URL there. We're now saying the the
origin remote repo is good. Gonna be at this URL, we run git
remote dash v, we can see that the origin repo for fetching is
at this branch. And the origin repo for pushing is also at this
branch. So when we want to get new code, a fetch code will
fetch it from here. And if we want t
o push code up, we want to
give code to the GitHub. We'll also get it from here. So now
we've set the new remote with that remote URL. Now we're going
to actually push the changes to github.com. And the way we do
that is with Git push, and we pick which remote we want to
push to, and we're going to push the origin. And then which
branch we want to push to which we're going to push to Maine,
it'll probably prompt you for your username and your password,
and maybe your email and everything. Now, i
f
authentication doesn't work for you, for some reason, you can
come over to Settings. Or if you scroll down to developer
settings, Personal Access Tokens and create generate new token,
some token, give yourself repo access, write
access, and hit Generate token. And try to use this token as
your password. Instead, be sure to use the GitHub documentation
and the GitHub discussion associated with this course, if
you get lost or if something doesn't work, as shown here. But
once it's done, once you
add all that information in correctly,
you come back to your get up and you will have your first GitHub
repo with all the code and everything in it like this. And
once you complete this step, once you do this, you should
absolutely celebrate. If you like, you can shoot a tweet web
three community in the blockchain community is
absolutely this collaborative space. So Twitter crypto is
where a lot of these people congregate to share ideas. So
definitely be sure to celebrate and share this and be
really,
really excited. And shoot a tweet out like this. Give your
friends a high five, share it on Twitter, share it on Discord
shared on Reddit, be excited for how far you've gotten, we've got
a lot more to go. But by completing this part, you have
done fantastically, and I'm so excited for you to start the
next section. Now, we're not going to go over the TypeScript
addition to this because there's nothing really new here.
However, again, if you want to see TypeScript, feel free to
jump into
the GitHub repository associated with this course. Alright, awesome. You've just
completed Lesson Seven, the heart had fun me. And now it's
time to move on to lesson eight, which is going to be our HTML
slash JavaScript to fund me, you can find all the code for what
we're about to go through, of course, on my GitHub repo. And
for this section, we're now going to start to see some of
the differences between Node js between that back end
JavaScript, and JavaScript in the browser or front end
JavaS
cript. And if you come to the GitHub repo associated with
this lesson, our main version we'll be using what's considered
better front end JavaScript. But we'll also have a no JS
addition, as well, if some of the front end JavaScript is
really confusing, and you'll see what I mean with some of those
differences very soon. Now, people can programmatically
interact with our smart contracts at any time. However,
most of our users are not compete developers. So we need
to create a website, we need to
create a user interface for them
to interact with our smart contracts and interact with our
protocols. And that's what this section is going to teach us,
it's going to be an introduction to building these fullstack.
building these front ends on top of our smart contracts. Now, I
wanted to show you what this is actually going to look like when
we finish it. Because here, we're actually going to make our
first front end our first website using the blockchain
using web three. And it's going to be
an incredibly minimalistic
website. As you can see right here, we're not going to have
any styling, we're just going to show you how to get the
functionality. And additionally, we're going to do a couple of
things that aren't really recommended and are definitely
not best practices. The reason we're going to do it like this
is the same reason that in math class, before you learn the
tricks for derivatives, you learn what a derivative actually
is. We're teaching it like this first, so that you ca
n
understand what's going on on the websites when you interact
with them. And when you work with them. We saw already with
Foston, touching the link where we can connect our wallets and
we can work with the faucets. All decentralized applications
have this website and have this setup where you connect your
wallet and then you interact by clicking buttons, which make
these function calls to the blockchain. And here's going to
be our minimalistic website that does exactly that. So this
section is
just going to teach you what's going on under the
hood. So you can really understand how to build these
applications at a professional level. So for this section, if
you don't want to code along with me, you definitely don't
have to however, coding along with me will definitely ingrain
everything in your memory here. So here's what an application is
going to look like. We have our website here, which is connected
to our hard hat, our local blockchain, but it's gonna run
exactly the same as if it
was on a real test net, the first thing
you'll notice is in our meta mask, we are not connected. And
we'll go ahead and hit Connect, and meta masks will pop up
asking us if we want to connect, we'll go ahead and connect to
it. And now we'll be able to interact with our heart at
Funmi. You'll notice two buttons here are functions that we're
familiar with, we have our withdrawal function, which is
going to be our withdrawal function that we just created.
And then of course, we also have our fund
function here where we
push or we send Aetherium or Matic or whatever, native
blockchain token to our smart contract. So we can do it
through this user interface. So once we're connected, if we want
to see the balance, we can actually right click, hit
Inspect, come over to our console. And we'll print out to
the JavaScript console, the current balance of our smart
contract. So nobody has funded this yet, we can come down, we
can choose an amount we want to fund. So for example, maybe 0.1,
eath,
we'll go ahead, we'll hit fund, meta masks will pop up,
we'll get a little council saying funding with 0.1. And
it'll give us all the transaction details that we need
to send 0.1 eath, to our smart contract, we can go ahead and
hit Confirm. And after it's been confirmed after it's been mined
on our local blockchain, we had get balance, we now see that
it's 0.1. We could call fund again, we could have multiple
funders, we could switch between different accounts and fund with
different amounts. An
d we can see that funding amount
increase, then we can call the withdrawal function. As long as
we're the owner, we can confirm and we can pull out all the
money out of our funding contract. And we'll get balance.
Now we'll see the balance is reset to zero. So this is what
we're going to be building. Are you ready? I sure am. Let's get into it. This is the
introduction to building websites with web three. All the
information here is available in our GitHub repo, so feel free to
follow along ther
e. Alright, so if you're in your heart head
Funmi dash Free Code Camp repo, we're still going to want to
have this open as well. But we're also going to want to
create a new Visual Studio code for working with our new repo.
So let's go ahead and CD down a directory. We'll type MK dir,
we'll call this HTML, Fund Me Free Code Camp. We'll cd into
that. And we'll open this up by typing code period. You can also
do File Open Folder, but we just want to open this up in a new
Visual Studio Code. New VS
code will pop up but before we flip
over to that, we do want to CD down CD back into hard hat fun
we Free Code Camp because we are still going to use everything in
here. We're still going to deploy In a smart contract using
this folder and using this repo, when you're building daps, or
websites that are connected to the blockchain, you'll usually
have two repositories or repos. One is going to be for the smart
contracts, like what we see here. This is our repo that has
all the code for our smar
t contracts. And then you'll also
have one for the front end slash website. And it's going to be
the combination of these two repos, which makes up the full
stack. So when people are talking about full stack,
they're talking about the smart contracts, which is going to be
our back end, plus, plus our HTML slash JavaScript slash
website stuff, which is going to be our front end. So smart
contracts are the back end, HTML slash JavaScript slash website
stuff is going to be our front end. So we have
our back end
already. And now we're going to build our front end, we want to
keep this up because we're going to need it to test and interact
with our front end. Awesome. So we have this new folder now,
HTML Funmi, Free Code Camp. Now this course is not a how to
learn front end course, we are going to teach you a number of
front end concepts. But if you want to learn a full traditional
front end course, once again, you can check out Free Code
Camp, they've got a ton of fantastic tutorials, I'm
teaching you front end, if you go ahead and follow along with
me, though, you'll definitely get a basic understanding of
front end as well as front ends and how it relates to our smart
contracts. Additionally, you don't have to do the front end
parts or the full stack parts. If you only want to take this
course, to learn back end and to learn JavaScript, and to learn
solidity and learn how to do these smart contracts
programmatically, then you can absolutely skip these front end
parts. However,
if you want to learn to build exciting
websites, and you want to have other people other than
developers interact with your protocols, you definitely want
to watch this part. Now before we actually jump in here and
start writing our code. We need to understand what exactly is
going on when work with one of these websites that use the
blockchain. So I actually made a video about this recently. So
let's watch a segment from that really quick, just so that we
can get up to speed with with exactly w
hat's going on behind
the scenes of these websites that interact with the
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 m
ight 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 Avi right, I'll hit Connect on the
application, it'll say, hey, how would you like to connect? I'll
choose Metamask. I'm going to change my Metamask to main net,
but you get the picture, right? This is 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 right click, hit Inspect. And
on the right side, we're gonna see 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 localhost. And we'll
also see meta mask 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 meta mask thing here is because I've meta mask
installed, right? The reason I see Phantom here is because I
have the Phantom app installed. Meta masks, of course, being an
EVM, wallet and Phantom being a Solana 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, we scroll down to here in the console, again,
you can find console, you can click here
and 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 dot Aetherium.
We also see an object here. Now this window dot Aetherium object
only exists if you have a meta mask or meta mask like browser
or if you want to look at some other web three wallet you do
window dot Solana. So Alana right and we see this win
dow dot
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 and right click 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 Solana source here. And if we go to
console, we still see window if I let me zoom in a little bit.
We still see window here. But if I do window dot Aetherium If we
get nothing
, or if I do window dot Solana, we also get nothing.
So in order for our browsers to know that there's a Metamask, 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 note. And you might have
seen URLs from
alchemy, or in FIRA 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 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 Solana or their wallet as the main wallet.
Now, there are a ton of other different types of wallets to
connect, like ledger, mu, 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 metamath Does it is
with window dot Aetherium.
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 the HTTP RPC URL
connection of the blockchain No, 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 meta mask
right. This is connected to in Fira, these are all connected to
in Fira. It's all the exact same thing. Meta mask just has a
really nice way of taking that URL, sticking it in the browser
for us in this window dot Aetherium or window dot Solana
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 l
et's apply it. So in here, let's make
a quick readme.md. Just so we can talk about what we're going
to be making here. So in this section, we're gonna be using
raw HTML slash JavaScript in conjunction with our smart
contracts to build this website. Later on. We will use next Jas
slash react, which is a more modern stack to build our
websites here. But learning understanding how to do
everything with HTML and JavaScript first, is going to
make our lives a lot easier come later on down the road. B
ut as
we know, all websites use HTML as kind of their scaffolding for
what they look like. So let's go ahead and create our HTML for
our website, we'll call it index dot HTML. And this is going to
be the basic scaffolding or the basic bones of what our website
is going to look like. Now, in VS code, if you go ahead and
just type exclamation mark index dot HTML, and you click the
first thing that pops up, it'll automatically populate your code
your file here with some basic HTML setup. If it does
n't do
this for you, feel free to copy paste the basic setup from the
GitHub repository associated with this course, we have our
doctype. HTML, we have some HTML tags telling us that everything
in between here is going to be an HTML, which is great.
However, for simplicity, we don't need most of this. So
we're going to make this a little bit easier. We're going
to delete this line, this line and this line. And we're just
going to change the title to fund me app. And then inside of
our body, we c
an do something like or Hello. And now we have
the bare bones to create a website just with this. Now to
show this on a website, we can do one of two things. If you are
using Visual Studio code, I'm going to recommend you install
the extension live server. And it looks like this. And I'll
have the extension ID for this extension in the GitHub
repository associated with this course, this is going to allow
us to easily spin up an HTML website. So we'll go ahead and
install this. And if you're not
using Visual Studio Code, I'll
show you a different way in just a second. Once this is
installed, you should have this little go live button at the
bottom. And if you don't, you can always open up your command
palette, which again, you can open up by hitting View command
palette, and you can type in live server and just say open
with live server but we're going to just click this go live
button. And it's going to say starting and it's actually going
to open up your browser with our index dot HTM
L. We can actually
see our website is being called Save on 120 7.0 point 0.1. This
is known as the loopback, or local host endpoint, we're on
port 5501. If you're not familiar with the ports, don't
worry about that for now, we have our index dot HTML here.
And if we change this to something like what's good, we
hit save. If it doesn't automatically refresh, we'd come
over here and refresh. And we'd see that being reflected here.
If you've never created a website before, you've
essentially just d
one it, congratulations, you might get
this.vs code folder. a.vs code folder allows you to make
settings specifically for the repo that you're working with,
for your code editor for VS code, but we're going to mostly
ignore it for now. Now, if you're not using Visual Studio
Code, what you can do is you can just run this in the browser. So
one thing you could do is you could right click it, and I'm
using a Mac. So I'm going to hit Reveal in Finder, aka reveal
where it's located. And you can just
double click it, and boom,
now it's running right in your browser. Instead of pointing to
your localhost, it's going to be pointing directly to your local
file path. Now one final version that we could do that I'm going
to highly recommend you don't do. But it's another option,
we're actually going to download a package for you using no Jas,
which allows us to serve up HTTP. And we're gonna install it
the exact same way we've installed our other packages, we
can do yarn, add dash dash dev HTTP h
yphen server. And you may
still want to add it anyways. But now, we'll get some node
modules for this HTTP server package, we'll get a package
json, and of course, a yarn dot lock as well. And what we can do
is we can stop this down here, stop that live server. And if we
go back to our website, refresh, it'll now be blank. And we can
run yarn HTTP server. And this will do the exact same thing.
And we get, and if we come over, and we refresh, we'll see what's
good. Now this one is a little bit mo
re finicky. And after you
make a change, like, Hey, what's good, you might have to close
it, and then reopen it and then refresh. So I do recommend that
if you're on Visual Studio code, you definitely just use this
little go live button, because it'll reflect your changes a lot
nicer. So let's go ahead and hit the Go
Live button. Hey, what's good pops up. Okay, cool. Our HTML is
working perfectly. So the title, of course, is
going to be the Funmi app, which we see up here in the title
section. S
o let's update this HTML, so that it has those
buttons, and it can actually connect and work with our
blockchain and work with any blockchain. Something else that
you can do in HTML is you can actually write JavaScript inside
your HTML. And the way we can do that is by doing this script
tag, and then we'll do a closing script tag. And anything inside
here inside of our script tags is going to be JavaScript. So I
could do something like console dot log, hi, with a bunch of
exclamation marks, I'm
going to save it. We'll go back to our
front end, we're going to right click, we'll hit Inspect, we'll
go to the console. And we can see that Hi prints it out, we
refresh, we can see the hi consistently printed out hi from
script tag, we'll save it, we'll move back, we see hi from script
tag printed out. I know it's a little bit small. So let me zoom
in. Alright, great. So we can type our JavaScript in here,
it's inside the script tag is where we're going to write our
JavaScript to write the fun
ctions that our front end is
going to interact with. Now, as we saw before, in this little
console, we can check for window dot Etherium, to see if Metamask
or is installed. And again, a lot of what we're working with
is actually right in the Metamask documentation. If you
go to their basic section, they talk a little bit about the
provider, which is this window dot Aetherium, you can read how
to actually interact directly with meta mask in the meta mask
documentation as well. Now using window d
ot Aetherium is just one
of the ways we're actually going to connect to the blockchain,
there's actually multiple ways because there's multiple
different kinds of wallets out there. But for now, we're just
going to pretend that window dot Aetherium and meta mask is the
only extension out there. So what we want to do is we want to
check to see if this wind of that Aetherium exists. This is
the first thing that we should be doing. Because if this
doesn't exist, this means that they can't connect t
o the
blockchain, one of the first things that we're going to want
to do is we're going to want to check to see if that exists. So
we can do something like if we can say type of window that
Aetherium does not equal, undefined. Then we'll do console
dot log, I see a meta mask. So now if we save, we come back to
our front end, we do see I see a Metamask. I've got a Google
Chrome up without Metamask that if we look in the console, and
we hit refresh it the same URL where our live server is
running.
We don't see that I see a meta mask because it doesn't
see a meta mask. We can do else. console dot log, no Metamask
refresh, we still see ICD a meta mask where we have a meta mask.
You don't have to open up a browser without one but we see
no meta mask for Chrome because it doesn't see a meta mask. Now
what we could do is We can automatically try to connect to
meta mask if we see that there is a meta mask, right? Remember
how before, when we hit that connect button meta mask popped
up and said
, Are you sure you want to connect? So what we can
do is, and you can, again, you can find this in the meta mask
docs, we can run this eath request accounts method, which
is basically going to be how we connect our Metamask. Now, this
is specified by a new EIP and an older documentations. And in
older tutorials, you might see Aetherium dot enable, which
essentially does the exact same thing. So what we could do here
is we could say, a cerium, or excuse me, window dot Aetherium
dot request. And w
e could put method F request accounts. And
we'll save that. Now if we go back to our browser, we'll
actually see, you'll actually see Metamask, go ahead and pop
up and say, let's connect so we can choose an account. And we'll
hit Connect, will automatically connect our meta mask to our
website. And now if we look at our meta mask, we can see this
little connected thing. It's saying that our account one is
now connected to our website. This means that the website can
now make API calls to our met
a mask, we still have to be the
ones to approve them. But it can go ahead and connect and try to
run transactions, which is awesome, which is what we want.
If you want to disconnect, we can go ahead and click that
little button and hit disconnect this account. Well that we have
our code currently is anytime we hit refresh, this is going to
pop up which is going to be really annoying. So what we're
going to do instead is we're going to wrap this up into an
asynchronous function, the exact same as
we've been doing, we'll
do we'll create an async function called Connect. And
we'll wrap it up these curly braces here. And then we'll just
format this a little bit to make it look nicer. And now if we
save, we go back to our website, and we refresh. And we go ahead
and disconnect. If we refresh Metamask won't
keep asking us Hey, do you want to connect Hey, do you want to
connect here do you want to connect, because we need to call
this connect function where we can do that is we can add a
litt
le button here. So right underneath our script tag, we're
going to add a button tag. So this is the opening button tag.
And then here's the closing button tag. And inside the
opening button tag declaration, we'll give it an ID, which will
be Connect button. And we'll say on click equals the Connect
connects function. And we'll call this button connect in
between these little button tags, we'll call it connect will
say when we click it will call the Connect function. So if we
save, and we go back
to our front end, we can now see we
have a little Connect button. And now if we press Connect,
Metamask is going to pop up, we'll hit next and connect like
that. And boom, now we are connected. And we can even do a
little await here so that we wait for this to finish before
moving on. And then after we connect, we can say console dot
log connected. So let's actually go ahead and test this out,
we'll go back to Metamask. we'll disconnect here, try to never be
on Main net if we don't have to be.
And let's go ahead run
connect, we'll hit Next connect. And now we see a little console
dot log come out saying connected. Okay, great. We can
also update our website accordingly. So that we can let
users know that we're connected. So we can grab the connect
button element ID and say that we're connected once we're
connected. So after we await to be connected, we can go ahead
and do document dot get element by ID Connect button. And then
we'll say dot inner HTML equals connected like that. And t
hen
instead of saying no minimize down here, we'll do just the
opposite. So we'll copy this line. And instead of connected,
we'll say please install meta mask. And we'll save we'll go
back to our front end, we'll hit connect. And now if we're
already connected, it'll just automatically go to connected.
If we're not connected, it'll pop up, we'll get connected. And
now we have this button that says connected, which is great.
So now we already know that we're connected. So we've
connected our Meta
mask to our front end. Now we want to
actually go ahead and do some functions here. And this is
where we want to create some more functions and some more
buttons that are going to use ethers that package that we
become so familiar with. Now, as we code, our script section is
going to get bigger and bigger and bigger. So oftentimes, we
actually want to put our code in a JavaScript file itself. So
we're gonna go ahead, come over here, we're going to create a
new file, and we'll call it index dot j
s. And instead of
putting our JavaScript in these script tags, we're going to put
it in this index and then import this index file into our HTML.
And the way that we're going to do that is we're actually just
going to grab this function. We're going to copy it, delete
everything for now. We're going to paste it into our end ext dot
j s, and then in our index dot HTML, we're just going to tell
our script tag to use index.js. So we're just going to say the
source is going to be equal to dot slash
index dot j s. And
then we're going to say, the type of this is going to be text
slash JavaScript. Now if we save that, and we save our index dot
HTML, we come back to our front end, we do a little refresh. And
we make sure that we go ahead and disconnect. And we do
another refresh, we can see that even with our index.js in a
different file, because we're doing src equals index.js, and
we're importing it into our HTML, when we hit Connect, it
still calls our Connect function. That's how we can
kind
of separate our JavaScript into its own js file that we're a
little bit more familiar with. Now, if you look in the GitHub
repo associated with this course, and you look in the
index dot j s, you'll see our Connect function, we've added
some quality of life stuff, we've added some try catches
just to make handling errors a little bit better, you can go
ahead and add those try catches in yourself if you'd like. But
I'm not going to demo them in this video here. Now we want to create our
fun
function. And then later on, we're going to create our
withdrawal function. And this is where front end JavaScript code.
And no Gs are a little bit different. In no JS, we've been
using this require keyword to import dependencies in front end
JavaScript, you can't use require, and it won't exactly
work. Now later on, we're going to use the Import keyword, which
is really the better way to do this. And this is where our
first differences going to be using the Import keyword for
front end is much
better than the require keyword, especially
since the require keyword doesn't actually work. And for
those you who might struggle with this disconnect in this
change. Once again, in the GitHub repo associated with
this, we do have a Node js edition of this, where you can
use the require keyword, you just have to go through the
readme and download some packages and run some scripts
that basically transform your code that uses require into code
that works with imports. But we're going to teach you
the way
to work with your front end code here using imports. Now you'll
see when we get to next Jas that we will still download code from
node modules and using a yarn lock and a package json etc. So
summary in future seconds, we are still going to do yarn add.
But outside of a framework when we're using this raw JavaScript,
this raw HTML setup, like what we're working with here, we're
not going to be doing yarn, add node modules will add node
modules for framework like next react. But for raw
JavaScript,
we'll be using a different syntax, which I'll show you
soon. So let's go ahead and start building our fund function
here. To make our fun functions, what would we normally do? Well,
we'd create an async function called fund. And in this
function, we probably would want to take some ether mount as a
parameter, because we're going to want to fund it with some
amount of Aetherium. When we call this function, we might
want to run console dot log will do a little string interpolation
here
. Funding with eth amount, dot dot dot, these semicolons
are going to drive me absolutely insane. So I'm going to add a
our dot prettier dot our rc file into this, and I'm going to go
ahead and add prettier in here. Otherwise, I'm going to lose my
mind. So we're gonna do yarn add dash dash Dev, prettier, just so
we can format our JavaScript with prettier come back to
index.js. I'm gonna hit command S, and it looks like it does
indeed, auto format with prettier now yay, no matter
semicolons, we c
an call this fun function the same way we call
connect. So in in our index dot HTML, maybe we'll create a new
button. Button. We'll give it an ID of fund will say on click
equals fund. And we'll say fund. And this will be our button
here, we save it, we're looking at our front end. Now the fund
button that if we call, we say funding with undefined because
we're not passing it an amount here. So back in our index
console dot log funding with and we'll just want to make sure
that we can actually c
all that Funmi function. So we'll copy
this line again and we'll say type of window dot Aetherium
does not equal undefined. We'll go ahead and try to fund here to
send a transaction. What are the things that we absolutely 100%
Always need? Well, we need a provider slash connection to the
blockchain. And we need a signer slash wallet, slash someone with
some gas to actually send it and then And we're probably going to
need the contract that we are interacting with. And to get
that contract, we're
going to need ABI and address. And with
these all together, we can send any transaction. So to get our
provider, we're going to actually go ahead and work with
ethers again. Now, we're going to do it a little bit
differently, though, before, the way we worked with ethers is we
said const ethers equals require ethers, right. And this is how
we pulled ethers. And now like I just said to you, though,
require doesn't work in the front end. And we actually don't
want to install ethers with a node mo
dules package. So what we
can actually do instead is let's go to the ethers documentation.
If you go to the Getting Started section, scroll down, they have
a section about importing using Node js, which uses require or
imports. And then they also have some documentation for working
with the web browser. So instead of us doing a node module, what
we'll do is we'll copy the ethers library to our own
directories and serve it ourselves. So what we do is we
can come in here, we can copy this massive
file, which is
ethers, but in front end addition, and come back to our
file. And we'll make a new file in here. Called ethers. We'll do 5.6
point esm.min.js. And we'll paste that massive thing in
here. Now, since I have prettier when I save it, it's going to
auto format. And it's just this huge file, which has everything
ethers, but front end defied if you will. Now what we can do is
we can import this into our index.js. So instead of using
require in here, we'll say import ethers from and then
we'll just refer to that file that we just got ethers dash 5.6
point esm.min.js. Now, we only need to do this weird copy
pasting of the file import thing in this HTML JavaScript lesson.
In future lessons with Node js, we are going to do yarn, add
ethers, kinda like we've normally seen. The frameworks
like React and next Jas that we're going to use are going to
automatically convert those yarn added packages to their front
end to five versions. But for this section, this is how we're
going to act
ually import the ethers package. Now the other
thing we'll have to do is on our front end, we'll have to change
this from type text slash JavaScript to type module
changes. The type module allows us to import modules into our
code, which we're going to be importing this, and we're going
to import another module as well. Awesome. And now though,
if we go back to our front end, do a little refresh with the
connect button, we get Connect is not defined at HTML
button.on. Click, so instead of callin
g our Connect button from
the front end here, we're going to remove these on clicks from
our index dot HTML, and go into our index.js and add those
connect buttons in here. So we'll say const. Connect button
equals document, dot get element by ID, Connect button. And then
we'll say const. One button equals Doc, you meant dot get
element by ID. One button, the idea of the connect button is
Connect button. The idea the fun button is fun button. And then
we'll say connect button.on Click equals con
nect. And one
button.on Click equals fond. We go back to our front end, do a
little refresh. We'll hit connect now. And it's actually
working, we'll go to our Metamask will disconnect,
refresh, hit Connect, and boom, it's popping up again. This is
just due to that type being module, if it was text slash
JavaScript, that on click button adding in here, but since we're
doing module, we're going to add those on clicks right in our
JavaScript. But now that we've got ethers in here, what we can
do is
we can even do like a little console dot log, just
paste ethers in here, or actually better yet. We'll add
it right above the Connect button. Go back to our front
end, we'll do a refresh and we see the entire ethers object
right in our front end which is perfect, which is exactly what
we want here. And since we've got these two variables here, we
might as well update this to just say connect button dot
innerHTML equals connected and here as well. Connects button
dot innerHTML equals please inst
all meta mask because now
connect button is going to be the same as running this
document dot get element by ID right here. and great. Let's go back
to continuing our font function. So we'll say const provider
equals new ethers dot providers dot web three provider, window
dot Aetherium. Web three provider is an object in ethers
that allows us to basically wrap around stuff like Metamask is
web three provider is really similar to that JSON RPC
provider, which we use before, which is where we put
in exactly
that endpoint, our alchemy endpoint, or when we're working
with Metamask. Here, whatever endpoint that we have in our
network section is web three provider takes that HTTP
endpoint and automatically sticks it in ethers for us. So
this line of code basically looks at our Metamask and goes,
Ah, okay, I found the HTTP endpoint inside there, Metamask.
That's going to be what we're going to use as our provider
here. Since our provider is connected to our Metamask. Here,
we can get a signer
or we can get a wallet just by running
const signer equals provider, dot get signer, this is going to
return whichever wallet is connected from the provider,
which again, our provider is our Metamask. So if we're connected
with with account one, it's going to return account one as
the signer connected with account two, it'll return
account two, etc. Now, I'm going to add console dot log signer
here, and then flip to the front end now and show you what
happens when we hit the fund button. You ca
n see in here we
have our JSON RPC signer, the signer is going to be the
account that we've connected to our front end. Now we have our
provider, we have our signer. Now, we're going to need our
contract by getting the API and the address. So we're gonna need
to say const. Contract equals what? How are we going to get
our contract? Well, this is where we're going to need to
know the ABI and the address of a working with typically, what
you'll see a lot of projects do since once a contract is
dep
loyed, the addresses are going to change is they're going
to have some type of constants file. So they'll create a new
file called constants, dot j s. And in here, they'll add the
addresses and any API's and anything like that for us to use
in our fund piece here. Now, as we're developing, and as we're
building, this, the backend and the front end team are gonna
have to interact a little bit, or if it's just you doing the
full stack, you're gonna have to interact with their back end. So
this is
why it's so important to have both your front end and
your back end code nearby. So if we go back to our Hardhead, fund
me project that we just made, we can find the ABI in here. Once
again, if we go to artifacts, go to contracts, we can go to fund
me dot soul, fund me dot JSON, we can find the ABI right here,
it's going to be this massive thing right here. So you can go
ahead, you can even copy this whole thing in this little non
squiggly bracket and this little bracket here, we can copy that.
And then we can come back to our constant.js. And we can just
save it as variable, we'll say Export const, Avi, e equals and
paste that in there. And then back in our index dot j s, we
can import it with import API from constants. Oops. Okay,
great. So we have the API. Well, what about the address, since
we're going to be running this locally, we want to get the
contract address of this locally run contract, we can do that a
couple of ways. One way is you can just have two windows open
one with
your front end code, one with your back end code, and
the one with your back end code, you can run yarn, hard hat node,
which will spin up our blockchain node for us, and give
us the address in here. Or what we can do. And this will
probably be a little bit easier is in your window with your
front end code and create a new terminal. And now we'll have two
terminals running. And then this second one, we're going to CD
down CD dot dot, and then CD, hard hat on Me Free Code Camp.
And in here, we're
going to run yarn, art head node. And this is
going to spin up our local blockchain in this second
terminal in here, where we have deploying fund me deploy that
address, blah, blah, blah, right here. And just a reminder, if I
hit this x, it just hides the terminal. I can always pull it
back up with terminal new terminal, and I can always hide
it, but this is still running inside of my terminal. So I'm
going to hide it. And what I can do is I can come back over to
our constants folder, and at th
e top, we can do export const
contract address. equals, we'll pull the terminal back up. And
we'll go grab, where it says, pulling fund me deployed at
right here. We'll copy that address, put some quotes, and
we'll paste it in. And now we're also exporting the contract
address, then in our index.js will import the contract address
with comma, contract address. And now we have both the ABI and
the contract address. So what we can do, we can say const
contract equals new ethers dot contract of, we
'll pass the
contract address, pass the API, and we'll pass it the signer.
Now we have an ethers contract object that's connected to our
signer, and we have the contract address, and we have the
contract abi, now that we have a contract object, we can go ahead
and start making transactions the exact same way we've made
transactions before we can just run const transaction response
equals a weight contract dot fund. And then we'll pass a
value of ethers dot utils dot parse, ether ether mount. So
this is going to be how we're going to go ahead and create our
transaction. And if you take this right now, and we go to our
front, we give it a little refresh, make sure we're
connected and we hit fund, we're gonna get this error value must
be a string. That's because at the mount right now is being
passed in as nothing. Now normally, we'll pass parameters
directly to our functions. But what we're going to do is for
now is we're just going to hard code this. So we'll say const.
At the mount equ
als, we'll do like 77, or something we had
fun. Now, what do you think's gonna happen? Well, we get this
other air, insufficient funds for intrinsic transaction cost,
or you might get some other error, but you're gonna get a
weird error here. And that's because we're not actually
connected to our local hard head node right now. Well, if we look
at our Metamask, we're currently not connected to the right
blockchain, we're connected to rink B, or main net or whatever,
we need to get connected to o
ur local host. If you look in your
networks, you'll actually have a local host object here already.
But let's just be super specific. And we'll add a new
network here, we're going to add something called our hard hat,
local host. And we'll hit Add Network and we'll add Hardhead.
Local host in here, the RPC URL, we can find from our node area,
which we scroll to where our node is running, we can copy
this URL and paste it into new RPC URL, chain ID is going to be
31337 currency symbol is going to
be go or ether. Even though
this pop up says it might have a different currency symbol go,
we're just going to put eath in here. And there is no block
explorer, right? Because this is a local blockchain, we're not
going to have a block Explorer. So we'll go ahead and hit Save. And now we have an account here,
which is great. We're connected to our local blockchain. And if
we switch our meta mask, we can see we're on the local hard hat.
And we are connected. Awesome. Let's refresh. Let's run fun
d
one more time. And we'll see a transaction does indeed pop up.
This is great. But our account here doesn't have any money we
have, we're broke, we don't have any local Hardhead Aetherium. So
we're actually going to need to import one of our accounts from
hard hat into our meta mask, which we can do so and you can
actually do this for any account with a private key so hard that
gives us these accounts. And we're going to import the
private key of Count zero into our meta mask. So we're going to
copy the private key, we're going to come back to our front
end, click on our meta mask, we're gonna hit this big button,
we're gonna hit import account. It's like type private key, and
we're gonna paste our private key here. A quick note, if you
choose json file, remember how back in that ether section, we
encrypted our key into a JSON file with a password, you can
actually import accounts with that JSON file with the
password. So if you encrypt a key and you want to add it to
meta mask, you c
an go ahead and add it in just like this. But
for now, we're going to use private key, we'll paste the
private key in and we'll hit import. And we can see we now
have an account, a new account and account three, with a ton of
eath from our local blockchain. So we'll refresh one more time,
we'll go ahead and connect, we'll make sure that our account
three is connected. And if it's not, we'll go ahead and hit this
Connect button. So that now our account three is what's
connected here. And we'll go
ahead and hit fund. And we now
see that we can fund this contract, we can go ahead and
hit Confirm. And if we look and nothing's going to happen on our
front end, because we didn't tell our front end to do
anything once we confirm, but if we go to our blockchain, we can
see our fun function was called we've just made our first
transaction on a blockchain from our own front end, this is
awesome, great work. But it's probably a little confusing to
the user. If nothing happens here, they're going
to hit the
fun function. And it's going to be like, Oh, okay, cool. What,
what happens? Now, we probably want to make it a little bit
more obvious that something just happened. And one more thing I
want to show you, you don't have to follow along here, if I hit
fund, and then I hit reject, it's gonna freak out on us and
be like, hey, like, you hit reject, I don't know what to do
now. So we're going to make our code a little bit more robust by
adding a try catch. So we'll tell JavaScript to try r
unning
this transaction. And then if it catches an error, just to
console dot log, that error. So now, if I hit fund, and I hit
reject, it's gonna, it'll still be a little bit mad, but at
least we're catching it and it's not going to break and destroy
everything. Okay, cool. We've got a transaction response here.
And when we hit fund, our front end goes great. You've funded me
I'm I'm super confused. Oh, one other point, something that
you'll probably run into multiple times. As we're doing
this
. If you get an error that looks like this eath. JS query
while formatting outputs from RPC, nonce to high expected not
to be too but got form, you will definitely see this a whole lot.
Here's what you do to fix this. The reason this happens is
because you've closed your hardhat node and then restarted
it, and your heart had node goes, Okay, well, I'm starting
fresh, I'm starting from zero. But Metamask isn't smart enough
to know that, what we want to do is we want to come to our
Metamask, we'll
hit this big button, we'll go down to
settings, we'll go to Advanced, and we'll go to reset account.
And yes, we're going to reset it. This isn't something ideally
that you'd like to do with an actual account with actual money
on a local network, this is fine. Now if you reset the
account, and you reset the node, we can go ahead and hit Confirm.
And it doesn't give us that error anymore. So that's kind of
the tip there, you want to reset the nuts so that our Metamask
and our blockchain are in s
ync with that nonce number. So we have this transaction
response. And we probably want our front end to give the user
some indication, hey, the transaction went through. So
what we want to do is we want to listen to the blockchain for
this to finish. So we can either listen for the TX to be mined.
Or we can listen for an event. We haven't learned about events
yet. But we will. So for now, since we haven't learned about
events yet, we're just going to listen for the TX to be mined.
Or to listen f
or the transaction to be mined, we're actually
going to create a new function called function, listen, or
transaction mine in this is going to take as input a
transaction response, and a provider. Now you'll notice this
isn't an async function. For this section. This is
intentional, we don't want this to be an async function. And
you'll see why in a second. We're going to be using
JavaScript promise JavaScript async functionality to its
massive advantage. And this is why JavaScript actually work
s so
well on the front end is because of how it's asynchronous. So
we're going to create this function listen for transaction
to be mined, we're going to await in our fund function. And
we're going to have this return a promise. Let's go ahead, and
let's learn how to build this. So we're gonna say console dot
log, string interpolation, we'll say mining. And then in here,
we'll say transaction, response dot hash. All of our transaction
response objects have a hash, which just represent that hash.
And we'll do a couple of data dot, we'll put in our console
here, we're waiting for the transaction to be mined, then
what we're going to do is we're going to return a new promise.
And the reason we're going to return a promise is because we
need to create a listener for the blockchain. We want to
listen for this event to happen. But we want to tell JavaScript,
hey, wait for this thing to finish looking, wait for this
thing to finish looking. Now here's where this gets a little
bit tricky. In o
ur fund function. After we create the
transaction, we basically want to tell JavaScript Hey, wait for
this TX to finish. So our code is going to look as simple as
await listen for transaction mine. And we're going to pass it
the trans action response and our provider. So we're
saying hey, listen for this transaction finish. And we're
using this await keyword, because again, the await keyword
says okay, we're gonna stop right here. We're gonna stop
until this function is completely done. Now in t
his
listen for transaction in mind, we have to define how we're
actually going to listen for this. So we're going to say
listen for this transaction to finish. Ethers actually comes
with a way for us to listen for transactions and listen for
events, which again, we haven't learned About, but don't worry
about that yet. So we can go to the ethers docs. And we can look
up once. Contract dot once. There's a whole bunch of
listeners that we can use to listen for events and listen for
different thing
s to happen, we can do this thing called
provider dot once where we listen for some event. And once
that event fires, we call some other function that we've
defined. Now, we haven't talked about events yet. And again,
don't worry about this quite yet. One of the events that we
can wait for is we can just wait for the transaction receipt to
finish, right, because once we get a transaction receipt, that
means that the transaction has actually finished going through.
So we're going to use this prov
ider dot once syntax to wait
for the transaction receipt, which is going to be our event,
which isn't really an event, but don't worry about that yet. And
then we're going to call some listener function that we
defined, you can also do provider.on, which will trigger
anytime your event fires provider once just triggers one
time, we only care about this transaction going through one
time. So we're passing our provider object. So we're gonna
say provider dot once our event which is just going to b
e
transaction response, hash, provided on transaction response
dot hash. So once we get this hash, which will pretty much get
right away, we're going to call our listener function. Now we
can create a function listener like this, and then just pass
listener in here. But we're going to do an anonymous
function here, because that's typically what we see as the
syntax for these loops. And so far, we're doing provider dot
once to do this anonymous function, we're going to do just
two little parenthe
ses here. And this arrow notation. So this by
itself represents an anonymous function. So we're saying, hey,
there's some function, it doesn't take any parameters, and
it doesn't have any code, this arrow function, this whole thing
is saying this is an anonymous function. So we're saying
provided at once transaction hash happens, here's the
function that you want to execute. It doesn't do anything
right now. But let's have a do something. So once this
transaction dot response finishes, we're goi
ng to take a
trans transaction received as an input parameter, or our our
callback function, or our listener function. And all we're
going to do is we're gonna say console dot log, completed with
a little string interpolation, transaction receipt, that
confirmations, confirmations, and then completed with
transaction receipt, confirmations, confirmations. So
once this provider dot once sees that there's a transaction hash,
it's going to give as an input rammer to our listener function,
the trans
action receipt, kind of that same syntax that we've been
seeing this whole time, once a transaction response finishes,
we get the transaction receipt, and we can do stuff with it. And
we see how many block confirmations it has. For us,
this pretty much should always be one. Now if we save this, go
back to our front end, and we hit fund, it's going to work,
we're going to give the user some indication that it worked,
which is great. But it's not really going to work the way
that we want it to wor
k. We have this console dot log done right
after we do the await, listen for transaction mine, and we
come back and we hit fund, we hit confirm, it actually doesn't
go in the order that we want it to go, it goes mining this thing
that it says Done. And then it says completed with one trip
confirmation what what's going on here, we should write
completed before we write done, because that's the order that we
have this in. But it looks like it's words, it's going out of
order. What what's going on
here, what's going to happen is
when we call listen for transaction mine, or listen for
transaction, mine function is going to kick off, but it's
going to kick off provided on once as its own process. So
await listen for transaction, mine will kick off the listener,
but it doesn't wait for this listener to find the transaction
response. So this function will actually finish before provider
dot once finishes. So after it kicks off the listener, it'll
run to the next line of our code, which is co
nsole dot log
and done. Our front end we'll go oh, oh, you kicked off a
listener earlier. Let me go back down and let me recheck to see
if it's finished. And if it has finished, I'll go do what it
told me to do. And this is where what's known as the event loop
kicks in. We don't actually wait for this
provider that wants to finish, we add this provided at once
onto this queue called the event loop. And our front end is going
to periodically check back to it to see if it's finished. So we
want to
adjust our code. So we wait for the listener to finish
listening, which is where we're going to get into promises here.
So what we want to do is we want to adjust this function to now
return a promise. And we're going to use this syntax a
couple of times in the future. So what we're going to do is
we're going to say hey, we want to wait for the listener To
finish listening, we're going to wrap this whole thing into a
promise. And we're going to say return, new promise. And a
promise takes a fun
ction itself as an input parameter. So again,
we'll use kind of this anonymous function notation. And it will
take two input parameters resolve and reject. So resolve
says, hey, if this promise works correctly, call this resolve
function. And for us, this promise is going to be done when
the listener finishes listening. And then we would reject if
there was some type of timeout, we're not going to write the
reject function. But in the future, if you were to do this
for production, you'd add some
timeout as the reject parameter.
Basically, you're saying, hey, once the listener finishes
listening, we're going to resolve and if it takes too
long, we're gonna say ask for you, you took too long, and
instead of closing it off, here, we're going to close it off
around this provider thing. So we're gonna say return new
promise, resolve, reject. And only once this transaction gets
fired, are we going to resolve this promise, like so. So what
is happening here, so we're going to put the resolve
inside
of this provider at once. So we're saying, once this
transaction hash is found, then we're going to call this
function, we're gonna say console dot log, and then we're
going to resolve so this promise only returns once a resolve or
reject is called. And we're telling it only resolve only
finish this function once. Transaction response dot hash is
found, because it's going to be inside of these little squiggly
parentheses for provider dot once, the promise right now only
resolves after it'
s fired. It's event here. If this was really
confusing, just copy paste this and move on, we're getting a
little bit deeper into front end stuff here. So hopefully, this
was clear, if not definitely jump in to the GitHub
discussions to start asking about this stuff. But now that
we've updated this, we can come back to our front end, we can
hit the fund button. And hopefully this time, everything
will go in order. We'll hit confirm, we see mining
completed, and then we see done. And the reason fo
r this is
because again, our await keyword is now waiting, it says oh,
you're returning a promise, I need to await I need to wait for
it to resolve or reject. And we only resolve the promise once
our provider finds this transaction hash and this
transaction receipt, this is this listen for transaction
mine. In future sections, all of this is going to be abstracted
away for us. So life is going to be much easier, but it is
important to understand what's actually going on here. Awesome.
So now we'
re giving the front end some indication of what's
going on. Our fun function is done well, no, not really. Why
not? Well, because right now we're hard coding the eath
amount to 0.1. And on the front end, we probably don't want to
hard code it, we probably want to allow users to fund as much
or as little as they want. So we actually need to change this
from just a button to a input form. So to do this, we're gonna
go back over into our HTML. So we'll go back to index dot HTML.
And we're going to
change this fun section here, we're going to
add some form information. So we're going to keep this button
as it is, or we're going to add like a little text box to input
as much eath as they want. So I'm going to create a label,
we're gonna say it's for, it's going to be fund and this label
we're gonna say, is eath amount, and then we're gonna close
label, this is basically just going to create a label. And if
we go back to our front end, we now just have this Etham out
label that isn't labelin
g anything. Now we're going to
create an input, and we're going to give it an ID of eath amount.
And we're going to give it a placeholder of 0.1. And then we're gonna
close the input. So now if we flip to our front end, we have
eath amount, labeling this little text box with a
placeholder of 0.1. And we can add stuff in here, you know,
blah, blah, whatever. And one more thing, if you have some
tags, but you don't put anything in between them, you can
actually shorten it by just putting the littl
e closing thing
at the back of it like this. So if you don't have anything
between your tags, you can just close them like this, these two
are equivalent, boom, boom, boom, boom, those are the same
thing. Great. When we called fund right now we're calling
fund and we're not passing any parameters. Let's update our
fund to no longer be hardcoding eath amount in here, and we'll
have and we'll have our fund function populated by whatever
we put in this eath amount input box in our fund function,
in
stead of saying const eath amount equals 0.1. We'll grab it
from this eath amount ID and we'll say document dot get
element by ID eath amount, dot value. So we're going to grab
whatever value In this input box here, now, in our index.js, we
have console dot log funding with eath amount, if in our
front end, we do 1.7. And we hit fund, we now see in our console
dot log, it says funding with 1.7. And we'll get 1.7. And our
little fun section confirm, it will mine it, it'll complete it,
and then sa
y, done. Awesome. So now we've added a little text
box here. So people can fund as much or as little as they want.
Perfect, this whole thing can kind of be considered a form.
There's also a form tag, but we're going to just use this for
now. All right, great. We're doing a lot of funding right,
and we keep funding our contract with more and more Ethan, we
keep adding more and more stuff, we probably want a button to
keep track of how much is actually in here. So let's just
add a really simple ba
lance button. And let me actually just
move this down to the bottom here, much better. So we'll say
button, id equals balance button. We'll call it get
balance. Now we have a get balanced button. And it's not
going to do anything because we don't have a get balanced
function. So let's go back to our index.js. And we'll create a
get bounced function async function get bounce. And we'll
just do what we've been doing so far. If type of window dot
Aetherium does not equal, undefined, then we're goin
g to
do const provider equals new ethers, ethers, dot providers
dot web three provider of window, that Aetherium then will
do const balance equals awaits provider dot get balance,
contract address, which again, we're importing way up at the
top, and then we'll just do console dot log ethers dot utils
dot format, ether, bounce this format ether function ethers
utils, which you can find in the documentation to make reading
ethers, formatted numbers much easier to read. Then same as
what we've been
doing before, we're going to go up, we're
going to copy this fun button line, paste it will say balance
button equals document dot get element by ID, balance button.
And then we'll say balance button on click equals get
balance, we're gonna come back to our front end, we'll do a
quick refresh, we'll do get balance. And now we can see the
balance in the console, we can fund with more like 1.8, we'll
hit fund metamath pops up, we'll go ahead and confirm mining
completed done, we hit get balanced,
and now we're at 3.8.
Fantastic. Alright, let's keep it going. What do we need to do
next, we're gonna go ahead and eat our withdrawal function
here. So let's create that this is gonna look really similar to
everything we've done so far. So on our index dot HTML, let's
create a new button that we can do by copying this whole line,
paste it underneath, call this withdraw button, we'll call
withdraw. And we'll call this withdraw. And then in our index,
do the same thing, we'll copy this, paste it
below. So with
drop button was document get element by ID, withdraw button,
withdraw button.on Click equals withdraw. So let's make a
withdrawal function down at the bottom. We'll go ahead, say
async. Function withdraw, will do the exact same that
we've done above. And I can even just copy everything from the
get balance and just remove the balance section. If type of
window dot Aetherium does not equal to undefined, then we'll
grab the provider here. And same as the fund contract, we'll grab
t
he signer and the contract. So let's just grab those two lines.
Paste them down here called signer equals provider dot get
signer. Contract equals new contract, blah, blah, blah.
Okay, cool. Now we're gonna do the exact same here as we did
with the fun function. So we're gonna do a little try, catch,
and then we'll even add the catch here, catch error, console
dot log error. And inside of our try, we'll do const transaction
response equals await, contracts dot withdraw. We don't need to
put any
parameters in here. Oh, then actually, let's do a little
console dot log, withdrawing, and then we'll get this
transaction response. And then we'll listen for this
transaction to get mined as well. By running a Wait, listen
for transaction mine transaction response comma, provider And
that's it, since we can reuse the functionality from our
listen for transaction mine. So now we'll refresh, we'll make
sure we're connected, check the current balance, which is 3.8.
Now we'll go ahead and withdraw.
We see our little withdrawing
console dot log, we'll hit Confirm. And it looks like we've
completed it, if we look in our Metamask, our bounced will have
been increased by the amount that was added here. And now if
we hit balance, we do indeed see zero, we can even double check
that this is actually working, we can see our balance is 9.99.
Let's go ahead and even fund this with 99 eath. Good confirm,
looks like it's completed. Our meta mask now shows 99.00. And
if we go ahead and withdraw, conf
irm, that withdrawal has
gone through, we can see our balance is back up to 9999. And
now let's just go back and change. Hey, what's good. We'll
refresh our front end. And now we can see you've done it, we've
created a minimalistic website that allows us to connect to the
blockchain and call functions from a front end. And from a
website, this is absolutely massive, you should be
incredibly proud and incredibly excited with yourself. Now, this
is definitely a minimalistic version. And we're goin
g to work
with more powerful and more modern front end frameworks. But
this will give you an idea of exactly what's going on behind
the scenes when we're working with those more advanced front
ends, and we'll style them up. So they look a little bit better
than this. But with that being said, you've just learned the
basics of how these front ends work. And now that you know how
to push things up to get, I highly recommend you start
pushing all of these projects up to your GitHub, they will be a
record that you've actually done the work and you've built these
smart contracts. And if you're looking to get an internship
down the line, if you're looking to help other GitHub projects,
this will be a record of hey, I can do this, what I can build
look at what I know how to do so congratulations on your first
full stack or your first front end application. Before we move
on, let's do a quick refresher of everything we've learned
here. So number one, typically, you want the repository or your
code base. For your back end for your smart contracts to be a
different repository than your front end, your front end code
is going to be a combination of HTML and JavaScript. When we
have a wallet like meta mask, we're injecting these browser
based wallets into the window object of our browsers like
window dot Aetherium. Each browser will have a different
extension here, reason we do this, we want to connect to the
RPC URL that's built into our meta masks. And in this way,
we're making the sam
e API calls to an RPC URL as we do in hard
hat as we do in remix as we do in ethers, etc, we created our
first promise based function where we had a listener kickoff,
and we wrapped it in a promise to say, hey, we want to wait for
our listener to finish since this is a promise based
function, we set a weight listen for transaction mind and we
waited for this transaction to finish so that on our front end,
once our transaction finished going through, we can tell the
user it's finished going throu
gh and we can continue doing other
stuff. Give yourself that round of applause and let's move on to
the next section. All right, welcome to the next
section. We are now on Lesson Nine, our hardhat smart contract
lottery, which again, all the code for this section is going
to be available in the GitHub repo associated with this
course. Alright, so let me show you what we're about to build.
I'm going to show you the front end login, you don't have to do
the front end, if you don't want. But the fr
ont end does
give us a nice way to visualize the lottery that we're building.
Here, we're building an application that allows users
completely decentralized to allow us to engage in a fair, a
verifiably random lottery. This is the application that would
actually fix the McDonald's issue that we talked about much
earlier. So first, we got this Kinect Wallet button that we're
going to click to connect to meta mask. And here, we're
actually going to show you how to connect to more than just
Metamas
k wallet connect to trust wallet, math, wallet, any of
these wallets, and we'll show you how to customize even more.
So we're going to connect to Metamask. Here, we'll choose our
account that we imported in from Hardhead, we choose Next we're
gonna choose Connect. And remember, for our front end bit,
we're gonna go to Settings Advanced reset account, if we're
working with a brand new hard hat blockchain. Now, the front
end doesn't look super nice, but we have an enter raffle button.
And we have
a little bit of UI talking about the current number
of players and then the most previous winner of a raffle. So
we can go ahead and enter the raffle and allow anybody to pay
0.1 eath. To enter our smart contract, we get a little
transaction saying transaction complete, and we get the current
number of players is one, we can continue to enter the raffle and
anybody can enter this raffle. And this, the smart contract
will keep track of all the players in here, we're going to
run this on a timer,
the lottery is going to automatically
trigger somebody to win. And to do this, to get a pure
verifiable random number, we're going to be using chain link VRF
to get a pure verifiably random number. And then we're gonna use
chain link keepers to trigger the automation to automatically
have one of these winners get picked every time one of those
time intervals is up. Once the keepers kick it off, they will
pick a winner, our decentralized lottery will say the most
previous winner is so and so. And
they will get all the money
from this lottery making a perfectly fair decentralized
lottery, we're going to call our contract, raffle dot soul but
you can call it lottery dot soul or really whatever you want. And
we're going to make it look really, really nice. So now
we're back in our Visual Studio code. This is going to be the
project. If you learn this, you have the skills to learn all the
rest of the smart contract concepts, and you are going to
be able to do great things in the space. This
is going to be
your flagship project, this is going to be the one that you can
be the most excited and the most proud about for this tutorial.
So let's go ahead and let's create a new folder. So we're
gonna do MK dir, hardhat, smart contract lottery, FCC or smart
contract, raffle, whatever you want to call it, then we're
going to cd into our hard hat, smart contract lottery Free Code
Camp. And we're going to type code period, or we're going to
open this up in a new folder, however we choose to
do. So now
that we're in our new folder, we're going to create our new
hard hat project. So we're going to do yarn, add dash dash dev
hard hat. And we'll get our node modules are package dot JSON and
our yarn dot lock. Now we'll do yarn hardhat. To get started
with a new project, and we're just going to select create an
empty hard hat dot config dot j s because we know what a basic
project looks like. And we're gonna give this project the
customizations that we want to see, we're going to create
an
empty hard hat dot config. And now in here we have a blank
hardhead.config.js with almost nothing in here. So we're
starting completely from scratch. Now we're going to add
all of our dependencies in here. And oftentimes, you'll add these
sequentially as you build but we're just going to add them all
in one line, and there are a lot of them. So we've left a copy,
paste the whole section of the full blockchain solidity course,
J S, for you to just copy, paste, so you can install
everything in
one go. So grab that line, and it's going to
have everything that we've been talking about. And we'll just
hit enter, and we'll install all of these. And as you create more and more
projects, you'll get the feel of what you like for your
dependencies and what tools that you like to use. Remember, at
the end of the day, the tool that's best for you and best for
the job is the tool that you like the most. There never
really is a one tool fits all, there's almost always going to
be trade offs. Alr
ight, now that we have all those dependencies
installed, if we look at our package, JSON will have this
massive dev dependencies, we'll have everything in Node modules
will have everything in yarn dot lock. And of course, as we know,
in order for any of these to actually work, we need to add
them into our Hardhead config. Now, there's a lot of stuff to
add in here as well. So like once again, if you want to come
to the heart at smart contract, lottery, FCC, and go to the
Hardhead config, you can
just copy paste everything and place
it into your project so that you don't have to always type
everything out yourself. Meclabs Hardhead waffle anomic labs
started ether scan harder to deploy slitted coverage, or had
gas reporter heart had contract sizer, which we haven't talked
about yet. But we will Don't worry. And then require dot env
dot config. And as you all know, all these little semicolons are
popping up and those are going to drive me absolutely crazy. So
once again, we can create a
dot prettier, RC. And if you want to
copy this as well, from one of your previous projects are free
to do so. One thing that I added in here was a print width of
100. This just changes how long a line can be before it goes on
to a new line. That's the only difference here. Now if we go to
our config and hit save, they go away, thank goodness. Now if we
look at our solidity version, we're currently using zero point
7.3. Let's go ahead and update that to 8.8 or 8.7, or whatever
you want to use. No
w that we have all the basic setup, we can
begin coding our smart contracts. So we first need to
create a new folder called contracts where we're going to
store all of our contracts. And let's go ahead and create a new
file called lottery dot soul or raffle dot soul or whatever you
want to call it. I'm going to call mine raffle dot soul. And
you might see this indexing thing happened from time to
time. It's our hard hat, solidity extension, indexing all
of our node modules. So it knows how to hi
ghlight things and
knows how to work with everything in our solidity
files. So that's what happens when that pops up. Now before we
jump in and create it, let's figure out what we're going to
do. So we're going to create our raffle contract. And what do we
want people able to do? Well, we probably want people be able to
enter the lottery, you know, paying some amount, we're
probably going to want to be able to pick a random winner.
But we want this to be verifiably random, we want this
to be unt
ampered with a bowl. And we also want winner to be
selected every X minutes or years or months, aka we want
this to be completely automated. So we want to deploy the smart
contract and almost have no maintenance almost have nobody
ever have to touch it again. And it'll just automatically run
forever. This is the power of smart contracts. As we know,
since we're picking a random number, and we have some event
driven execution, we know that we're going to need to use a
chain link Oracle, since we'
re going to need to get the
randomness from outside the blockchain. And we're going to
need to have that automated execution. Because a smart
contract can execute itself, we need somebody to automatically
trigger these. So to trigger selecting a winner, we're gonna
have to use the chain link keepers. And that's pretty much
going to be our entire code. Now, I usually like to do this
before I start any project. And the reason that I do a little
bit of brainstorming is because we don't want to just
jump in
and really do anything, we want to have a good idea of what
we're trying to build so that we can write tests for it, so that
we can know if we're going in the right direction, etc. Now
that we have a good idea of where we're going, let's build
it. So per usual, let's do spdx license identifier. And my team
will do pragma solidity little caret here, zero point 8.7, even
zoom out, just a hair will trash that and we'll say contract,
raffle, we can even make sure that we're not going crazy
by
doing a little yarn Hardhead compile and compiled
successfully, we want it to be able to enter the lottery want
us to be able to enter it. So maybe we'll create a function
called enter raffle. What else do we want to do, we want to be
able to pick a random winner. So maybe we'll create a function
called pick a random winner, and boom. So let's comment out pick
random winner for now. And let's just work on this enter raffle
thing. In the past, we've created projects like Funmi,
where people ca
n send ether to our contracts or send whatever
native blockchain token to our smart contracts using the
message dot value based off of some USD value. For this one,
we're just going to have the entrance fee instead be USD
based, it's just going to be that native asset. So for our
enter raffle, we don't have to set a USD price, we can just set
a minimum eath price. So up at the top, let's pick our minimum
price. So we'll do a un 256 entrance fee. And now some of
our learnings from our last sectio
n should come in here, we
now know that this entrance fee is going to be a what, it's
going to be a storage variable. So let's prepend it with s
underscore let's make a private variable because we always want
to set our visibility. But let's have the entrance fee be
configurable. Well, let's create a constructor now. And we'll
have this entrance fee be settable in our constructor, so
our constructor will take a UNT two to the sixth entrance fee, s
underscore entrance fee equals entrance fee. Wel
l, if we're
gonna only set this one time, we might as well make this a
constant or an immutable variable. So let's make this an
immutable variable so that we save some gas we'll change this
from S to I and now we're saying you interviewed six private
immutable I entrance fee equals entrance fee. Now we probably
are going to want other users to see the entrance fee. So down
below, we can create function get entrance fee, and this will
be a public view function which will returns a UN to the set
N
ext, and we'll just say return entrance fee. Now we have a
function that users can call to get the entrance fee. But we, as
developers can use this AI entrance fee to know this is an
immutable variable. This is pretty cheap gas wise for us to
read from in our enter raffle, we've done a ton of these
before, all we need to do is we just need to require the message
dot value is greater than that I underscore entrance fee, what
we've learned before about those error codes, so we could use
require me
ssage dot value, or we could do one of these customers,
which is going to be a lot more gas efficient, because instead
of storing this string, we're just going to store an error
code in our smart contract. So let's do that. Instead, we'll
say if the message dot value is less than our eye entrance fee, then we're just going to revert
the whole transaction with some error code. And we'll use a best
practice naming raffle underscore underscore not enough
eath entered. And we'll grab this arrow code
. And we'll have
if the user doesn't send enough value will revert with not
enough eath entered now that we know they're calling into raffle
with enough value, we're probably going to want to keep
track of all the users who actually enter our raffle. That
way, when we pick a winner, we know who's in the running. So
let's create an array of players at the top error here. And then
just to make this look even nicer, we'll do a little comment
here. And we'll say state variables. And we'll combine
bo
th our storage and are not storage variables just in this
state variable section. So we'll do address array players. Now
players, of course, is going to have to be in storage, because
we're going to modify this a lot, we're going to be adding
and subtracting players all the time. So we're going to do s
players will make this private as well. And we're going to make
this address payable players because one of these players
wins, we're going to need to have to pay them. So we'll make
this address
payable private as players. And since we're going
to make this private, and it's probably good that we know who's
in the players array, we'll even do function it player. This will
be a public view that returns an address of one of these players.
And we'll just return s players of index will have this
function, take a un 256 index as an input parameter, we know that
players is going to be a storage variable. And we're going to add
it to our enter raffle Oh, and we definitely want our Inter
raffle
to be public and to be payable. Since we're having
people send message dot value, and we want anyone to be able to
enter our raffle. So it'll be public, it'll be payable, it'll
be perfect. Now that we have our array, and someone's entered the
raffle, we'll do s players dot push message dot sender. Now,
this doesn't actually work because message dot sender isn't
a payable address. So we'll need to typecast it as a payable
address just by wrapping it in payable. So now we have a way to
keep track
of all the players that are entering a raffle. Now one of the
concepts that we haven't gone over yet is actually going to be
events. And events are really important to our smart
contracts. Whenever we update a dynamic object, like an array or
a mapping, we always want to omit an event when we get to
less than 10. And then especially less than 15 with the
next Jas and if T marketplace, these events will make a ton of
sense, especially for front end developers. So right now events
might be a litt
le bit of a weird thing for you as we explain it.
But as we continue on, they'll start to make more and more
sense. So we're going to start adding events to our smart
contracts. Whenever we update one of these dynamically sized
data structures. And to learn more about events and how to use
them. We're gonna watch another video that explains all about
events. You can actually follow along with this video as a side
project. But let's learn all about events. Now, if you've
worked with solidity, you
've probably seen these things
called events before or maybe you haven't seen something like
events. But you've always wondered how chain link or the
graph or some of these other off chain protocols work under the
hood. And in this video, we're gonna learn about logging and
events in solidity, viewing those events on ether scan, and
then working with them in hard hat. Now it's the Ethereum
virtual machine, or EVM. That makes a lot of these block
chains tick, like Aetherium. And the EVM has this
functionality
called a logging functionality. When things happen on a
blockchain, the EVM writes these things to a specific data
structure called its log, we can actually read these logs from
our blockchain nodes that we run. In fact, if you run a node
or you connect to a node, you can make a F get logs call to
get the logs. Now inside these logs is an important piece of
logging called events. And this is the main piece that we're
gonna be talking about today. events allow you to print
informati
on to this logging structure in a way that's more
gas efficient than actually saving it to something like a
storage variable. These events and logs live in this special
data structure that isn't accessible to smart contracts.
That's why it's cheaper because smart contracts can access them
so that If the trade off here, we can still print some
information that's important to us without having to save it in
a storage variable, which is going to take up much more gas.
Each one of these events is ti
ed to the smart contract or account
address that emitted this event in these transactions. listening
for these events is incredibly helpful. Let's say for example,
you want to do something every time somebody calls a transfer
function. Instead of always reading all the variables and
looking for something to flip and switch, all you have to do
is say, listen for event. So a transaction happened, an event
is emitted. And we can listen for these events. This is how a
lot of off chain infrastructure
works. When you're on a website,
and that website reloads when a transaction completes, it
actually was listening for that transaction to finish listening
for that event to be emitted, so that it could reload or it could
do something else. It's incredibly important for front
ends. It's also incredibly important for things like chain
link, and the graph in the chain link network. A chain link node
is actually listening for request data events for to get a
random number, make an API call, or etc.
Sometimes there are way
too many events. And you need to index them in a way that makes
sense that you can query all these events that happen at a
later date. The graph listens for these events and stores them
in the graph so that they're easy to query later on. So
events are incredibly powerful. And they have a wide range of
uses. They're also good for testing and some other stuff,
but you get the picture. They're really sick. Now that we know
what events are, let's look at what they look like
how we can
use them and how we might use them in our smart contract
development suite. Now here's what an event is going to look
like. We have an event here called stored number. So we have
basically a new type of event called stored number. We're
saying, hey, solidity, hey, smart contract, we have this new
event thing, we're going to be omitting things of typed stored
number in the future. When we emit this event, it's going to
have these four parameters, it's going to have a unique 256. For
c
alled old number. Are you interested? Is it called new
number are you intimidated six called add a number and an
address called center. Now for the astute people here, you might have noticed that there
was another keyword in here, the index to keyword and this is a
really important keyword. When we omit one of these events,
there are two kinds of parameters, there are the index
parameters and the non indexed parameters, you can have up to
three index parameters. And they're also known as topics.
So
if you see a topic, you know that that's going to be an
indexed parameter. Index parameters are parameters that
are much easier to search for, and much easier to query than
the non index parameters. In fact, we recommend F get logs
function, it even has a parameter allows us to search
for specific topics. So it's much more searchable than the
non indexed ones. The non indexed ones are harder to
search because they get ABI encoded, and you have to know
the API in order to decode them. Now, th
is is told our smart
contract that there is a new type of stored number, a new
kind of event here, we need to actually emit that event in
order to store that data into the logging data structure of
the EVM. To do that, we need to do something that looks like
this. This is what it looks like when we emit an event, it looks
very similar to calling a function. So you call a mitt and
then the name of the event. And then you add all the parameters
in there that you like. Here's the full example of a
smart
contract that has an event and is going to be the example that
we walked through in hard hat. Now in this smart contract,
whenever anybody calls the store function, we're going to omit
this event. Here's an example of a transaction where we call the
store function with a value of one, let's look into the logs to
see what this event actually is going to look like an event is
going to be broken down like so the address of the contract or
account, the event is emitted from the topics or the i
ndex
parameters of the event data. This is the ABI encoded non
index parameters of the event. What does this mean? This means
that we took those parameters that were non indexed, we match
them together with their API or application binary interface,
pumped them through an encoding algorithm, and boom, this is
what we got. If you have the API, they're very easy to
decode. If you don't have the ABI they are very hard to decode
these non indexed parameters cost less gas to pump into the
logs. Now i
n this particular contract, since we have verified
the code, we verified the contract ether scan knows what
the ABI is, and we can view this in Deke or decoded mode. Hex
mode is obviously the non decoded mode, or in its raw, hex
or hexadecimal or encoded mode. You can read more about the
layout of these events in the solidity docs. Now, so that's
the basic introduction of events. And for those of you who
want to watch the rest of that video, and who wants to actually
practice using events yourse
lf, there's a link to these videos
and the code repository associated with that video, if
you want to play with it, and if you want to learn more, so feel
free Ready to refer back to the full blockchain solidity course,
Jas, if you want to go deeper into events now that we're back,
and we've learned a little bit more about events, let's add
some events to this contract. Remember, these events get
emitted to a data storage outside of this smart contract.
Let's create an event called raffle enter
a good syntax for
naming events, name events, with the function name reversed. So
for enter raffle, we're gonna say raffle entered. So up at the
top below our state variables, but above our constructor, we'll
create a new section called events. And we'll create our
first event. So we'll do event, raffle, Enter. And we'll just
have this raffle enter take one index parameter, it'll be an
address indexed player. So in our Inter raffle, we're going to
say omit raffle enter. And we're just going to p
ass it message
dot sender. I'm going to remove these comments for now. But feel
free to leave them in as you code along. Now in this part of
my raffle coding or my lottery coding process, I probably would
start already writing some tests and already writing some deploy
scripts. The reason that we do this is it's good to test our
functionality as we progress. And oftentimes, when I'm writing
smart contracts, I'm constantly flipping back and forth between
my deploy scripts, my contracts and my tes
ts to make sure
everything is doing exactly what I want it to do for the purpose
of this course. And just to make it easy for you to learn and
follow along, we're not going to do that. And we're just going to
keep writing our smart contract almost to complete and then move
to our deploy scripts and tests. So in its minimalistic sense, we
essentially have a way for people to enter our raffle. Now
we need a way to pick the random winner. And this is where we're
going to need chain link VRF and cha
in link keepers. So let's
again, watch some sub lessons about learning about chain link
VRF. And learning about chain link keepers. We've made some
videos about these before. So we're just going to play these
videos. So you can learn about how taling VRF version two
works, and also how chain link keepers works, then we're going
to come back and we're going to add them into our contracts
here. If you're already familiar, and you already played
with them, feel free to skip those sections. And we'l
l just
get to building them here. Hi, my name is Steven fluid. And
today we're going to be taking a look at chain links VRF. version
two, version two has a few different mental models that we
should be aware of. And I want to show you what it feels like
to be using it. The big important thing to know about
Vera version two is that instead of the Vera one model where
you'd be funding your contract with Link, instead, you're going
to be funding a subscription, which is basically an account
that al
lows you to fund and maintain balance for multiple
consumer contracts. Let's dive into the docs and see what using
Vera fie two looks and feels like. In order to show this off
a little bit. I'm going to dive right into the get a random
number guide in the chain link documentation. And so it's going
to go through a few the requirements of some of the
technology we're going to use today. And the first thing it's
going to ask us to do is to make sure that we are on the Rinkeby
test net. So let's go
ahead and jump over to Rinkeby. Make sure
my meta mask is unlocked here. And now that I'm on Rinkeby,
great, I should be able to use the VIP version to test net. Now
we're going to jump over to subscription manager. And the
subscription manager is where we're going to manage our
subscription account. Basically, this is the place that you put
the funds in order to be able to use it across a bunch of
different chains. So we're going to go ahead and connect our
wallet here in order to use the subs
cription app. And then we're
going to go ahead and create a new subscription. So we'll just
use my address as the subscription address here. I'll
approve the creation. And as soon as that transaction is
confirmed, our subscription should be created. Alright, now
we have a subscription. Basically, this is the account
where we're going to fund it. And then we can use that account
for all of our random Miss requests. So I'm gonna go ahead
and just put in 10 link here, you can put in however much yo
u
want. The price and link of every random number you request,
it's going to be based on the current gas rates on a given
chain, as well as the gasoline that you've chosen. On our funds
have been added, let's go ahead and add a consumer contract. So
it's asking us for consumer address, we don't actually have
a consumer address yet. So let's go ahead and jump over to the
documentation and create a contract that is going to
request a number. So if you scroll down, you're going to see
this Vera fee
to consumer that soul contract that we can open
it in remix, let's just jump right there. We're gonna notice
a few different things in this contract at the top, we've got
some input. So now you've got VRF, consumer base version two,
we've got an interface for the VRF coordinator, and then also a
reference to the link token interface. So all of those are
specified for you on the Rinkeby network here in the example
code. And then you can refer to the documentation for whatever
chain you're going
to be deploying to. And then you're
going to see a few new options here. So the key hash option is
the way that you specify that gasoline that was described in
the documentation. So depending on the key hash you choose for
the given chain you're on the gas limit will be set
differently for your random number requests. So for example,
on a theory main net, we have a 200 way key hash 500 Wiki hash
and 1000 graykey hash. You can also see in our contract Hear
that we have a callback estimate that yo
u're in charge of. So
depending on how much gas you're willing to spend in the fulfill
random number, you should set this value appropriately. Next
up is request confirmations. So this was something in verify
would be one that you could not control. But here now, depending
on the chain you're on, depending on the request and the
type nature of the request you want to make, you can actually
change this number. And then one of the most important and useful
features that gives you a lot more flexib
ility and control of
your VRF is you can actually specify the number of random
numbers you want. And so you specify the number words, and
then that will specify how many random you went to 56 as you get
back from the network. All right here in the constructor, we're
gonna see a address for the coordinator and address for the
link token. And then you'll see that the subscription ID is
going to be created as we deploy the contract. And so I'm gonna
go ahead and get that. So if you remember when we
created the
subscription after we found it, and we see this subscription ID,
and now when I deploy this, I'm going to use that subscription
ID. And we've got two methods that should look very familiar,
we've got fulfill randomness method that takes in randomness
is going to be fulfilled by the Bureau of Oracle, as well as you
got request random words, which is how we're actually going to
initiate the request to the Oracle. So I think we're
actually are already ready to go ahead and deploy this.
So let's
jump to the deploy screen here and select the right contract,
which in our case, is your FB to consumer. And I'm going to make
sure that I am on injected web three so that we can actually
deploy to the Rinkeby network. And I'm going to paste in the
subscription ID here. And I'm going to deploy, let's go ahead
and pay for that transaction. And as soon as that is confirmed
by the network will show up here, and we'll be able to copy
this address, and then add that as a consumer and author
ize this
contract to use my subscription account. So let's go ahead and
authorize this with them another Metamask transaction. Alright, we can have your
subscription, we can see how much link we funded it with. And
we can see our consumer contract. So now by doing this,
we've authorized our consumer contract to make requests for
randomness. So let's go ahead and make a request for
randomness here. So we're gonna go back to our contract that we
deployed here. And we're just gonna use the remix in
terface
here to keep things simple. And I'm going to request some
randomness. So obviously, this is going to use all of the
configuration that I specified in my contract, just kind of
hard coded here. So we're gonna get to words of random is here.
So we're gonna hit request randomness, confirm the Rigby
transaction. And then as soon as that transaction comes back, we
should notice that we actually have a request. And then what
we'll be doing is we'll be waiting for the Oracle to call
fulfill ran
dom words on our contract. And then we'll be
storing all of those random words in this s random words,
storage variable. Let's go ahead and check to see if our random
numbers come back from the Oracle. So I'll go in here into
random words. And let's request the zeroeth item of the array
looks like we've got a random number there. And because we
requested two random numbers, we should also have an item in
index one. All right, we we've got our randomness there. And if
we go back to the subscripti
on manager app, you're going to see
that there's actually an event history item here, we'll see
that we spent about point three, three link to get those two
random numbers, we've just taken a journey to see what it looks
like and feels like to use Veera version two, now that we've learned a little
bit more about chainlink VRF, I hope that you took some time to
go to doxa chain link and play around with it a little bit so
that you understand what's really going on, we're going to
use this sample
contract in here to create our function that's
going to pick our random winner. This is an on chain contract
that coordinates with a verifiably random chain link
node to give us a random number, you could look at the code
directly on chain, or you can come right to the chain link
GitHub and look at all the code for how this is actually
happening provably and randomly. And we'll have a link to this in
the GitHub repo associated with this course. So we're going to
create our function here called p
ick a random winner, this
function is going to be called by the chain link keepers
network so that this can automatically run without us
having to interact with it. And actually, while we're updating
this, I want to add some some stars here saying view slash
pure functions. Now our pick random winner function, we're
actually not going to make public we're going to make
external, external functions are a little bit cheaper than public
functions. Because solidity knows that our own contract can
ca
ll this, we're actually going to change the name of this
function, as well, very soon, but we'll get to that in a
little bit. So in order for us to pick a random winner, we
actually have to do two things, we first have to request the
random number. And then once we get it, do something with it. So
chainlink VRF is a two transaction process. And this is
actually intentional. Having random numbers in two
transactions is actually also much better than having it in
one. If it's just one transaction,
then people could
just brute force tries simulating calling this
transaction and we'll learn how to simulate calls soon simulate
calling these transactions to see what they can manipulate to
make sure that they are the winner. We want to make sure
that this is absolutely fair. Nobody can manipulate our smart
contract into having them be the winner of the lottery, this
function is actually going to request it. And then in a second
func the random number is going to be returned. And in the
transa
ction that we actually get the random number from the chain
link network, that's when we're going to actually send the money
to the winner. And if we go to the chain link documentation,
the function that the chain link node calls is this function
called fulfill random words, this is going to be the
requesting one, which we could even change this to request
random winner to make it more clear, and then we'll make a
function fulfill random words. And this is going to be an
internal override, which
we'll explain in a little bit, now
fulfill fulfill random words basically means we're fulfilling
random numbers. The word comes from a computer science
terminology, but you can basically just think of this as
fulfill random numbers, because we can get multiple random
numbers. Now in order to make our raffle contract VRF a bowl,
we have to import the chain link code, we can go back to the
documentation, and we're just going to grab this bottom line
and we'll grab the top line in a second. So we'
re going to do
import, I'm going to write it out. But if you want to copy
paste, you can at chain link slash contracts slash SRC slash
V 0.8, slash V RF, consumer base, b two dot Sol. And since
we're importing at chainlink, slash contracts, we're gonna
need to add that in by running yarn, add dash dash dev at
chainlink slash contracts. And now that we have this in here,
we should be able to import like so. And we're going to need to
make our raffle VRF consumer base double, we're gonna need to
i
nherit VRF consumer base go into our node modules at chain
link src, V 08 VRF, consumer base V two, it comes with this
function fulfill random words. And you can see it's an internal
virtual function, virtual means it's expecting to be
overwritten, the reason that it's in the sphere of consumer
base V two is so that the VRF coordinator, which we'll use in
a bit knows that it can call this fulfill random words
function. And this is the function that we're overriding.
So back in our raffle dot sal
t, we're going to inherit it by
doing is VRF consumer base V two. And now if we scroll down
to fulfill random words, we can add in the input parameters
fulfilled pill random words, which are going to be un 256,
Request ID and un 256, a memory array random words. And if we
hit Save Our linter will now notice, okay, this is what I'm
expecting, I'm expecting us to override the Fill random words,
which takes these parameters. Now if we look in our docs in
our constructor, we need to pass the VRF con
sumer base V TOS
constructor and pass that VRF coordinator. Again, this V RF
coordinator is the address of the contract that does the
random number verification. So right next to our constructor
will add the VRF consumer base V to constructor and we need to
pass the VRF coordinator v2 address. So in our main
constructor, we'll add that as a parameter as well. So we'll say
address VRF core to Nate Torre v two,
and then we'll pass this as a parameter for the VRF consumer
basically till now that we
have that we shouldn't see that
little underscore anymore. And we should be able to run yarn
aren't had compiled. Awesome. And we can see compiled two
files successfully and our code is working great. Now something
that I often do is I actually hate running yarn hard hat all
the time, because that's too many keys for my little brain to
work with. I would prefer to write as little keys as possible
hard hat also would like us to write as little keys as
possible. So hard hat comes with a shorthand
and autocomplete
hard hat shorthand is an NPM package that installs a globally
accessible binary called H H, that runs the project's locally
installed hard hat and supports shell auto completion for tasks.
So what we can do is we're going to start with NPM, like this,
but we're gonna install it with yarn, we're going to run yarn,
global, add hardhat shorthand. And what this is going to do, we
can see here installed hard hat shorthand with binaries H, H,
and Hardhead completion. Now instead of r
unning yarn,
Hardhead compile, which we can still run, we can just run h h
compile, running h h is going to be the same thing as if we had
just run the Hardhead command for our local directory. So now
we need to actually have our request random winner function
requests a random winner. So let's go back to the
documentation. And we'll see how to do that. We can look at this
function request random words and see exactly how it works. On
the Vera of coordinator address. We go ahead and call this
re
quest random words function, we're going to need to call this
function on the coordinator contract to get the coordinator
contract. We're going to use the Vera v2 coordinator interface
and the Vir of coordinator address. So we're going to want
to keep track of those. We can do that once again in Arkansas.
factor, we have the address being passed to our Bureau of
consumer base. Let's also keep it as a state variable for us.
First, let's get the interface so we can interact with that
contract, we
can import that from chain link as well by doing
import at chain link slash contracts, slash SRC slash fees
0.8, slash inter faces slash VRF cord to mentor V to interface
dot Sol. And now that we import this interface, same as we did
with price feeds, we can do VRF, coordinator v2, interface VRF
coordinator. And then we can save the sphere of coordinator
using the address so we can say VRF coordinator equals this
address VRF coordinator veto. And we're just going to wrap
that address around the
interface so that now we can
work with this VRF coordinator contract. Now we're only going
to set our VRF coordinator one time right in our constructor.
So what's the best thing that we can do here, private, immutable,
you Gosh, darn. Right, let's do private, immutable, you have
coordinator and we'll change the name to I underscore the ref
coordinator so that we know that your coordinator is indeed an
immutable variable. In order to request the random word, we need
to give it a number of paramet
ers, I'm gonna go ahead
and copy this line into our contract just so that we can
talk about exactly what's going on with it. And we don't need
the AES Request ID. And instead of coordinator we're going to do
I underscore fear of coordinator. So we're going to
call request random words on that VRF coordinator contract,
and we need to pass it the key hash or the gasoline, I prefer
calling it the gas lane, we go to the chain link, Doc's go to
contract addresses. And we scroll down we can see differ
ent
gas lanes a different configuration parameters for
different networks is key hash is going to be the gasoline of
the key hash, which tells the chain link node the maximum
price you're willing to pay for your guests and way if for
example, gas prices skyrocket, and it's going to cost you a ton
of money to get that random number setting a ceiling, we'll
make it so that random number doesn't come back. For us to
pick a gas lane, we're probably going to want to have this gas
lane or this key has
h stored somewhere. So let's go ahead and
make that a parameter of our constructor as well. And we'll
save that as a state variable. So we'll do comma bytes 32,
gasoline or key hash or whatever you want to call it. And then
we'll make a new state variable. And we're only going to set this
once. So we'll make this a private, immutable. I underscore
gasoline, and then we'll say gasoline equals gasoline. So now, we can just swap this
out, or I underscore gasoline. Okay, what's next, we need a
subsc
ription ID, the subscription ID is going to be
the subscription that we need for funding our requests.
There's actually a contract on chain, which we can use to fund
any subscription for any of these external data or external
computation bits. And in this contract, there's a list of
these subscriptions for people to make requests to. So we need
the ID of the subscription that we're using to request our
random numbers and pay the link Oracle gas, the subscription ID
is probably also going to be s
omething we're going to pass as
a parameter to our lottery. So once again, let's scroll up to
our constructor, we'll add a new parameter. And our subscription
ID actually doesn't need to be a un 256, it can actually be a
little bit smaller with a un 64. So we'll pass a un 64 sub
scription ID, we'll make a UNT 64. Since we're only going to
set this once we'll make this a private, immutable. I underscore
sub scription ID down here and we'll say I subscription ID
equals sub subscription ID. Alright
, awesome. So now we can
change this to our subscription ID. Okay, what's next? Request
confirmations request confirmations is a un 16, which
says how many confirmations the chain link node should wait
before responding. So if you make a request, and there's only
one block confirmation, maybe you don't actually send it
because you don't you're afraid of some type of blockchain
reorganization or something, we're not going to worry too
much about this. And we're actually just going to make this
a
constant of three. So we're not even going to have this be
per amortizable we're gonna have this one be a constant. So we'll
say a you int 16 Private constant request, confirmations
equals three, and we're using the capslock and underscores for
our constant variables here. And now we'll grab request
confirmations and stick it in right here. What's next callback
gas limit callback gas limit is going to be the limit for how
much gas to use for the callback request your contracts fulfill
random wor
ds, this sets a limit for how much computation are
fulfilled random words can be this is a good way to protect
ourselves from spending way too much gas. If, for example, we
accidentally code our contract in a way where fulfill random
words is incredibly gas expensive. It'll block the
random number from responding. We are going to make this
parameter sizable because we want to change it depending on
how we code our fulfill random word. So in our constructor,
let's add one more, we'll add you int
32. Since the size of
this is a un 32, callback, gas limit, and we'll save the sub
top as a UNT 32, private, immutable, I underscore call
back. Yes limit. And we'll save this. I call back gas limit
equals call back Yes, limit. And then we'll take this, and we'll
stick it here. All right, we got one more number of words, this
is going to be how many random numbers that we want to get, we
only want one. So we're gonna go back up to the top, we're going
to create a you int 32. And we'll make this a
lso a private
constant. Num words equals one, because we only want one random
number. And then that'll be the last thing we need to add for
our IVF coordinator dot request random words. Now this request
random words function returns a request ID a un 256 Request ID a
unique ID that defines who's requesting this and all this
other information. If we want to save it, we can do u and 256.
Request ID IVF coordinator requests random words. Now for
now, we are going to emit an event with this request
ID. And
we'll go over why we're going to do that a little bit later,
create a new event at the top. And we're not going to follow
the naming convention here. Because we're going to change
the name of our functions a little bit, we're going to call
this requested raffle winner. And we're going to take a un 256
indexed Request ID. Down here, we're gonna do omit requested
raffle winner Request ID. And now we have a function that we
can use to request a random winner using chain link VRF. Now
again,
we're going to set this up so that the chain link
keepers call this on an interval, but we'll do that in a
little bit. For now, let's figure out what to do once we
get that random number. Once we get that
random number, we're going to want to pick a random winner
from our array of players up here. So what do we do? Well,
let's go in here. And let's pick a random winner using something
called the module function. Now we're gonna get an array back of
random words, or random numbers, if you will,
since we're only
requesting one random word, this random words array is going to
be of size one with one random word in it. Now this random word
is a un 256. So this random word could be something like this.
Well, obviously without the hyphens, but it could be
something absolutely massive like that our players array is
only going to be so big. So how do we get a random winner from
this potentially massive random number, we can use something
called the modulo function, the module operation, a mod
N
yields, the remainder are after the division of an operand a by
the blah, blah, blah, blah, blah. So what does this mean?
Well, we can use the mod function to get a random number
out of our players array. So let's say our players array or
excuse me, our s players array is of size 10. And a random
number is 200. So how do we pick a random person out of this
players array, or, let's say our random number is 202. If we do
202, which is our random number, mod 10, we're gonna basically do
202 divi
ded by 10. But instead of having the decimals, we're
just going to say, Okay, how many times is 10 evenly divide
into 202. And what's the remainder, what doesn't divide
evenly into 202? Well, 20 times 10 equals 200, with two
leftover, so two doesn't evenly divide, or multiply into 200. So
that means 202 mod 10 is going to equal to two. So that's how
the module function works. And we can use that so we're always
going to get a number between. So we're always going to get a
number here between zer
o and nine, which works out perfectly
because that which works out perfectly, because those are the
indexes of the 10 people in our array. So let's use that here.
We'll say a un 256 index of winner is going to be equal to
random words, at index zero, because we're only getting one
random word module, the S underscore players dot length.
So this will give us the index of our random winner to get that
address of the winner will do address payable, recent winner
equals s players at this index of wi
nner so awesome. So now
we'll have the address of the person that got this random
number, the person's that's going to be our verifiably
random winner. Now, it might be kind of cool to brag to people
that you're the recent winner. So let's go ahead to the top and
we'll create a new state variable for our most recent
winner make this state variable in a new section called lottery
variables. We'll say address private s underscore recent
winner and it'll start out as nobody but as we get winners
up
date it with s recent winner equals recent winner and we'll
probably want people to know who this is winter is so down below,
we can do function get recent winner, it's going to be a
public view that's going to return that address. And then
we'll just say return s underscore recent winner, since
again, the recent winner is going to be a storage variable.
So now that we have a recent winner, what else are we gonna
do? Well, we're probably going to want to send them the money
in this contract. So
we're going to do exactly what we did before
with sending the money, we're going to do that bull success,
comma, blank equals recent winner, call, I'm gonna say
value, it's going to be address of this dot balance, we're going
to send all the money in this contract, and we're going to
pass it no data. And now we could say require, you know,
success, whatever, we're going to be a little bit more gas
efficient here. And we're just gonna say, if not success, then
we're going to revert a new transfer
failed error. So we're
gonna go to the top, or say, error name of the contract is
raffle, underscore, underscore, transfer failed, like that, then
go back down, and we can now do revert, raffle transfer failed
like that. And now that we've picked a winner, right now, we
don't have a way to actually keep track of the list of
previous winners. So we're just going to omit an event. So
there's always going to be that easily query double history of
event winners. So we're going to create a new event
in the events
section called the event winner picked. And this is going to be
an address indexed winner. And we'll scroll down and we'll do
MIT winner picked the address of the recent winner. This looks
pretty good. This looks pretty good here. Now
you'll notice our request ID as this little underscore here and
saying, hey, it's an unused function parameter, since we
don't use this, but we still need fulfill random words to
take a request ID and a random words array. But we don't use
Request ID
, we can just comment out just the request ID part
like this, this tells our function, hey, yes, we know that
you need a un 256. But we're not going to use the request ID. So
we'll leave it in here. But we'll leave it blank. Now let's
run a little compile here. We'll use yarn, hard hat, compile, or
hh compile, we'll see if we're coding things correctly. And
indeed, we don't see any errors. So perfect, we can continue. So
we've added the chain link VRF, where we have a way to
verifiably get a ran
dom winner. This is fantastic. Now let's
update this contract so that not only can it pick a verifiable
winner, but it can also do this all programmatically and
automatically trigger picking a random winner based off of some
time interval without us having to interact with it. And in a
decentralized context. In order for us to automatically trigger
smart contracts based off of some parameter be a time
parameter, maybe the price of some asset is some number, maybe
there's a certain amount of mone
y in the liquidity pool or
really whatever trigger that you want, we can use chain link
keepers. To do this. Steven fluid has done once again, an
amazing introduction to chain the keepers. So we're going to
follow along with another sub video section of Steven
explaining chain link keeper sauce, he's going to be using
the COVID test net, but be sure to use whatever test net is in
the documentation. When you play with this and you try this. My name is Steven foon. Today, I
want to show you how to
use the chain link keeper network in
order to automate your contracts and give them access to off
chain computation. Let's go ahead and dive in. So what we're
going to look at today is we're going to start on the chain link
documentation webpage. And if you just scroll down a little
bit, you're going to find using chain link keepers. Now there's
really two parts to building a chain link keeper up kept smart
contract. So the first thing is you need to write a smart
contract that's compatible by
implementing these two methods.
And then second, you want to register that smart contract for
upkeep with the channeling keeper network. So let's go
ahead and do both of those things. So let's start off just
by copying and deploying this sample code that we've got. With
this one click to remix, what we're gonna see here is a very,
very simple contract. That is just a simple counter. So we can
see it's got a counter here. So it's got just a simple number.
And then you're able to specify when you
create the contract and
update interval, and then the contract is going to verify Hey,
has enough time passed. And if it has, let's update the
counter. And you're going to notice that chinley compatible
or timekeeper network compatible contracts use two really
important methods that are part of this keeper compatible
interface. The first is check upkeep, and check upkeep is
special because this is where the off chain computation
happens. So this is a method that's not actually run on
chain. This
is run off chain by a node from the channeling
keeper network. And so what's really nice about this is that
the gas used here isn't actually gas, it's on chains. This is
just being run by a chain link node. And then what happens is
if your check upkeep method returns, the upkeep is needed.
Then it's going to go ahead and perform upkeep. On chain, so you
can actually generate data off chain, and then pass that in.
That's called the check data. And then that becomes the
performed data that's pass
ed into perform upkeep. And so the
PErforM upkeep method is where you're going to want to verify
that things are correct. And that things actually should be
modified and run on chain, and then actually make the state
change. And so let's go ahead and compile this contract and
deploy it to the COVID network. So let's go ahead and within
remix, we can do this compilation. And we're going to
compile and deploy directly to COVID. So the zookeeper network
is currently as of the filming of this, avail
able on both COVID
as well as the theory of main net. And let's go ahead and play
the counter contract. And let's see what's not updating more
than every 30 seconds. And so let's go ahead and deploy. So
meta masks can ask for a little bit of payment, in order for me
to deploy this contract to the COVID network. And it looks like
that is live. So now what I'm going to do is I'm going to be
able to take this keeper contract, and copy its address.
And now we're going to register that contract for u
pkeep. So
we're going to jump over to the application that powers the
champion keeper network, there's a few different ways you can
use, you can interact directly with the registry contract. But
there's a very, very nice interface that lets you do this.
So let's go ahead and register a new upkeep and it's giving me an
error that says you need to connect your wallet. So let's go
ahead and do that. So I'm going to just connect wallet here, and
I'm gonna give it access to my account. And then from
there, we
should actually be able to register. So I'm going to use an
email address here. I'll give my contract a simple name. I'll
paste in that address from the deployed contract, and then I'll
give it a gas limit. And then check data is this special thing
where you can actually register multiple upkeeps On the same
contract in passing data to specify how you want checkup
keep to be run of, we're just going to ignore that that is an
optional one. And then we'll give it a starting balance of
ar
ound 10 link, it's gonna go ahead and use Metamask again to
register that transaction on the network. And once it's
confirmed, my upkeep should be registered with the network and
funded with 10 Link To kick things off. Alright, if we go
ahead and view the upkeep, we can see it's registered. And as
soon as the next round of the keeper nodes executes, which
should be roughly about every block, we should see that the
check upkeep method is going to return that hey, upkeep is
actually needed because
the timestamp is more than 30
seconds ago. And then we should go ahead and perform upkeep. So
as soon as I take a look at this in remix, I can actually make
this bigger here, we're going to be able to see from the methods
of the contract, if we check the counter, it's going to start at
zero. And as soon as that 30 seconds has passed, we'll be
able to hit the counter again, we'll see that the channeling
keeper network has performed upkeep on my contract. Alright,
we just refresh and we see the b
alance of the upkeep has been
decreased by about point 01 link. And we should also see
within our contract that our counter has now updated via
proof that perform upkeep method call. And now our counter is at
one showing us that our contract is being approved by the
chainlink keeper network. And everything is working exactly as
we expect. So as you can see, it is very, very easy to create a
contract that is compatible with the keeper network. And it's
very easy to register that upkeep and start
seeing that
your contract automation and option computation are working
flawlessly. Now that we've learned a little
bit more about how chainlink keepers work if you want to take
some time to go through the documentation and open up way
down below this open and remix button. So you can actually work
with one of these and see these in action for yourself on a test
net, feel free to do so we're gonna be using a setup very
similar to this keepers counter setup in the chain link
documentation. So now
let's update our code so that this
request random numbers automatically happens using
chain link keepers. And if we look at the example contract can
actually read more about what's really important for this to
work. And we need a check upkeep and a perform upkeep function in
our code. So instead of request random winner, this is going to
be the PErforM upkeep that we're going to change. But first let's
make this check upkeep checkup keep function is basically going
to be checking to see is it t
ime for us to get a random number to
update the recent winner and to send them all the funds. So
let's go ahead and make that function. I'm going to add some
notes here, just so that it's clear what's going on. And maybe
I'll even do natspec To tell developers what's going on with
this function. So we're going to create this function check
upkeep. And if we look at what this needs, it needs to be
external override external override. And if we see this
override keyword, this means that okay, ther
e's probably a
perform upkeep somewhere else. And if we scroll to the top, and
we're going to import this keeper compatible interface, so
that we make sure that we implement both check upkeep and
perform upkeep in our code here. If you want you can just import
keeper compatible in here or just the keeper compatible
interface in our code. We're going to do import at chain link
slash contracts slash SRC slash V 0.8 point two interfaces slash
keeper compatible interface that soul and now we're just
gonna
say contract raffle is Veera of consumer base V two and keeper
compatible interface and this keeper compatible interface
inheritance just make sure that we add checkup keep and perform
upkeep, which we're going to add in a little bit. And if we look
back at the docs we can see checkup keep takes a bytes
called Data check data as an input parameter. So we'll do
bytes called data. Check data as a parameter. Now this check
upkeep bytes call data allows us to specify really anything that
we w
ant when we call this check upkeep function. Having this
check data be of type bytes means that we can even specify
this to call other functions, there's a lot of advanced things
you can do by just having an input parameter as type of bytes
for us though, we're going to keep it a little bit simple. And
we're actually not going to use this check data piece. So
similar to how below we're not using Request ID, we can just
comment it out. However, we still need to make sure that
this parameter is ty
pe of bytes called data. Now anyways, let's
go ahead and annotate this check upkeep function, we'll say this
is the function that the chain link keeper nodes call they look
for the to return true. Look back at the documentation, we
can see that this check upkeep returns both and upkeep needed
and to perform data, which again, we're going to ignore
this upkeep needed is going to be true or false. If it's true,
that means it's time to get a new random number following
should be true in order to re
turn true. So in order for it
to be time to request a random winner, what should happen, our
time interval should have passed, which we haven't defined
yet. But we will lottery should have at least one player and
have some eath. And then our subscription is funded with Link
similar to how with channeling Vir f your subscription needs to
be funded with Link, the same thing needs to happen for check
upkeep and keepers to run your subscription needs to be funded
with link. Now we're going to add on
e more additional piece
here, we're going to say for the lottery should be in an open
state, something that we want to avoid when we're waiting for a
random number to return. And when we've requested a random
winner. We're technically in this weird limbo state where
we're waiting for a random number to be returned. And we
really shouldn't allow any new players to join. So what we
actually want to do is create some state variable telling us
whether the lottery is open or not. And while we're wait
ing for
our random number to get back, we'll be in a closed or a
calculating state. Now what we could do at the top
of our contract, we can just say Boolean, private, s underscore
is open. And we can just set this to true if we're open
otherwise false. Well, what if we have a ton of different
states? What if we want it to be like pending, open, closed,
calculating, etc? What do we have a ton of different states?
Well, we could make this a U and 256. Private s underscore state.
And we could just
keep track of the state having like zero b
pending a one reopen to be closed, three, be calculating,
etc. But this can be a little tricky to keep track of. So a
better way to actually keep track of all this in our code is
to use an enum enums, can be used to create custom types with
a finite set of constant values. So we can create, for example, a
state created locked inactive, and it's basically a new type
for a smart contract for us, we're going to create a new
type. And if we go back to the l
ayout of variables, and our
smart contract types should actually be first thing in our
contract. So we're going to create an E new called raffle
state. And for now, and we're just going to have it be open,
we're just gonna have to be open or calculating. Now when we
create an enum like this, we're kind of secretly creating a un
256, where zero equals open, and one equals calculating. However,
this is much more explicit that we know what each one of these
numbers actually means. Now that we've cr
eated this new type
called raffle state, we can create a new lottery state
variable of type raffle state. So the exact same way we declare
any other variable, we'll name its type, which is going to be
raffle state. And this is going to be a storage variable. So
we'll go ahead and do private s underscore raffle state in our
constructor, right when we launched this contract, we
should open up this raffle. So we'll say s, raffle state equals
and we could use a un 256. Wrapped in type raffle state
l
ike so. Or we can be more explicit and say raffle state
DOT open. Now we know that the raffle state is in an open state
and we only want checkup keep to work is if the lottery is
actually open. Additionally, we probably only want people to be
able to enter if the law battery's open. So let's go
ahead and create another if statement and revert if the
lottery isn't open. So we can say if s underscore raffle state
does not equal raffle state DOT open, then we're going to revert
with a new error, we
're going to create raffle underscore
underscore, not open. And of course, at the top will create
error, raffle not open. Now additionally, let's go down when
we're requesting a random word. Let's update the state to be
calculating so other people can't jump in here. So right
above our VRF coordinator dot request, random words will do s
underscore raffle state equals raffle state DOT calculating so
that nobody can enter our lottery and nobody can trigger a
new update. And then once we fulfill af
ter we pick our
winner, we'll say s raffle state equals raffle state DOT open
again. Something else that we forgot to do was after we pick a
winner from s players, we need to reset our players array. So
let's add that in here as well as players equals new address
payable. array of size zero. So we'll reset the raffle state.
And we'll reset our players array. Alright, great. So now
that we've learned about enum, let's add it to our checkup keep
here, we're going to check these four things. And if
they all
pass checkup keys will be true and will trigger the chain the
keepers to request a new random winner. So first, we'll say bool
is open. It's going to be equal to raffle state. That open
equals equals s underscore raffle state. So you can think
of that as this boolean is open is going to be true if raffle
state is in an open state, and it will be false if raffle state
is in any other state. So great. We haven't is open Boolean that
we can check later on. What else do we need? Well, we n
eed to
check to make sure our time interval is past well, we don't
have a time interval yet. So let's create a time interval. In
order to check the time we can use another one a solidity is
globally available variables with block dot timestamp block
that timestamp returns the current timestamp of the
blockchain to get the current timestamp, we're gonna need
block dot timestamp. But to get if enough time has passed, we're
going to need to get the current block dot timestamp minus the
last block t
imestamp, which we don't have yet. Let's go ahead
and create a state variable to keep track of the previous block
timestamp. So this is going to be a new state variable that
we're going to make. I'm gonna say u and 256. Private s
underscore last timestamp. And right when we deploy this
contract, we'll update this with the current timestamp s last
timestamp equals block dot timestamp. Alright, awesome. Now
we have a last block timestamp. But we're going to need to check
that the difference betwee
n the current timestamp and the last
timestamp is greater than some interval. So we also need to
create an interval. And this is going to be some interval, this
is going to be some number in seconds of how long we want to
wait between lottery runs. So let's go ahead and add this to
our constructor as well. And do a comma here. And we'll do u and
256. Interval. And we're going to create another global
variable u and 256 Private s underscore interval. And in our
constructor and we'll say s interva
l equals interval. Now
interval isn't going to change after we set it. So instead of
making a storage variable, let's make it an immutable variable to
save some gas. Okay, perfect. Now that we have all this, I'm
actually create a boolean to check to see if enough time has
passed. So we'll say Boolean time passed equals the current
block that timestamp minus s underscore last timestamp. And
we should check to see that this is actually greater than I
underscore interval. So we have a Boolean check
to see if we're
open. It'll be true if we're open and we'll have a boolean to
see if enough time has passed. This will be true if enough time
has passed. What else should we check? While we should check to
see if we have enough players. So we'll do Boolean has players
equals and we'll check to see if s underscore players dot length
is greater than zero. If s players dot length is greater
than zero as players will be true, otherwise it'll be false.
And we'll also see if we have a balance so we'l
l do Boolean has
balance equals address. This dot balance is greater than zero.
Then finally, we're going to take all these booleans and turn
them into the return variable that we're looking for. We're
gonna say Boolean up keep needed. Equals is open and time
passed. And as players and as balance, like that, so all these
combined is gonna be this Boolean upkeep needed. And if
this returns true, it's time to request a new random number and
it's time to end the lottery. If this is false, it's not
time
yet, it's not time to end the lottery up. Now again, if we go
to the chainlink, documentation upkeep needed, actually needs to
return that Boolean upkeep needed and some bytes memory
perform data. So we need to update we need to update our
function here. And say returns full upkeep needed, comma, bytes
memory, perform data star slash. And since we've initialized
Boolean up, keep needed up here, we don't need to say what type
of upkeep needed is down here. Since this will automatically
get r
eturned performed data is something that we can use. If we
want to have checkup, keep do some other stuff. Depending on
how this checkup keep went. We don't really need it to do
anything else. So we can just leave it as such. Right. So now
we have a check upkeep, we have a way to check to see if it's
time to trigger picking our random winner of our lottery or our raffle. Now that we
learned how to actually do this trigger, let's write the
function that gets executed after this returns true. This
is
going to be our perform upkeep function, which we can see an
example again in the chainlink documentation. Now when it's
time to pick a random winner, actually, what we're going to do
is just we're just going to call this request random winner
function. So instead of having this extra function, let's just
transform our request random winner function into this
perform upkeep. Since once check upkeep returns true, the chain
link nodes will automatically call this perform upkeep
function. So in
function request random winner, let's rename this
to perform upkeep. And we'll have it take the input parameter
bytes called Data perform data bytes, called data, perform
data. In our checkup, keep we had to perform data, we would
automatically pass it to our performance keep, we're not
going to pass anything to perform upkeep, we can leave it
commented out like this. Since perform upkeep is actually
identified in the keeper compatible interface, this is
now going to have to override that funct
ion. Now we want to do
a little bit of validation before we continue here. Because
right now anybody can call our perform upkeep function. So we
want to make sure that it only gets called when checkup keep is
true. An easy way for us to do that is to actually call our own
checkup, keep function. Now right now, checkup keep it's an
external. So we actually can't call our own checkup keep
function. So let's change it to public so that even our own
smart contracts can call this checkup keep functio
n. Now that
we've made it public in perform upkeep, we can call checkup,
keep passing nothing, and then return the upkeep needed and
perform data which we don't really care about. So we'll get
we'll get that ball up keep needed. And then we don't care
about perform data. So we'll leave that blank equals check up
keep. And we'll pass it a blank called data. Now, we want to
make sure that this is true in order to keep going with the
function. So we could write a require here, but we're going to
do
if not up keep needed, then we're going to revert with a new
error that we create raffle. upkeep not needed. And we're
going to pass some variables to this error so that whoever was running into
this bug can hopefully see why they're getting this error. So
we'll pass the balance of this contract just in case there's no
ether in here. We'll add the players dot length, just in case
there's no players. And we'll add a un 256 s underscore raffle
state. Make sure that the raffles actually open. And
then
of course, we'll need to create this air at the top air, raffle
upkeep not needed, which is going to take a un 256 current
balance un 256 non players and you went to fit six raffle
state. Our code is starting to look really professional. This
is awesome. Now something that we forgot to do back in the
fulfill random words because we actually forgot to reset
timestamp every time a winner is picked. We want to reset the
timestamp as well so that we can wait another interval and let
people part
icipate in the lottery for that interval. So
we'll scroll down into fulfill random words. And right after we
reset players will also reset the timestamp. Okay great and I
think we're just about done here. Let's add a little bit of
natspec to make this look even more professional and and give
people who are reading our contract even more information.
So let's add title here at title and we'll say a sample raffle
contract will say an author is going to be me Patrick Collins
or you can put your own
name there as well at notice. This
contract is for creating and untampered orrible decentralized
smart contract. And then we'll do add Dev, this implements
chain link VRF v two, and chain link keepers. Alright, awesome.
We've got our type declarations, we've got our state variables,
we've got lottery variables, which are still state variables,
we've got our events. Now it's time for our functions.
Afterwards, we've done a little bit of natspec, at least on our
check upkeep. If you want to add s
ome more natspec on things like
enter, raffle, perform upkeep, etc, you can absolutely do so.
And then down at the bottom, we have our views slash pure getter
functions. Let's see, do we want any other getter functions here?
Well, we probably want to give people the chance to get a
raffle state. So we'll do function, get raffle state, this
will be a public, you returns a raffle state. We'll say return s
underscore raffle state, we probably want to give people the
chance to get the number of word
s. And this is going to be a
little bit interesting here running. So if we do function,
get num words, public view returns, you went to the six
return num words, you'll see something interesting happened
here. We pull up our compiler and run hh compile. Hopefully
everything works here. Oh, and everything doesn't work because
I didn't import this correctly. Let's fix that. Let's try again. Oh, there's a couple things I
missed. Let's fix es players. This is why it's good to compile
as you code as
players dot length. Let's try again. And I
spelt interval wrong. I underscore inter vol strike to
compile again, see how many more spelling mistakes I made. And
there it is. I underscore in error go to paste that and we do
get another error here. Invalid type for argument in a function
call invalid implicit conversion from literal string to bytes
called data requested. Since we're passing this empty string
here. And checkup keep needs a call data called data actually
doesn't work with strings. S
o we need to make this bytes memory
instead. And our compiler is now happy with us. And I spell it
the timestamp wrong. That's a lowercase s so and you might see
some squiggles here on Check upkeep, we could make this a
view function since we're not actually modifying any state.
But I want to keep it public for reasons I'll show you a little
bit later. But finally we get the the yellow squiggly that I
was looking for here. And if we run hh compile, we should see a
warning in our compiler as well
. Okay, so we see all those yellow
squigglies here. Unnamed return variable can remain unassigned.
We need this bytes memory in here because that's what the
keepers are looking for. Morning function state mutability can be
restricted to view for our function checkup key. You can
make a view if you want, but I'm going to keep it public for
reasons I'll show you a little bit later. And finally, function
state mutability can be restricted to pure this is what
I wanted to show you since num words is
actually in the
bytecode, since it's a constant variable technically isn't
reading from storage, and therefore this can be a pure
function. Returning num words doesn't actually read in
storage, it literally will go and read the number one. So
doing get num words in solidity with num words being a constant
variable, it's going to literally be the exact same as
saying get one and we would return one here, we might also
want to get the number of players. So we'll create a
function get number of pl
ayers. And this will be a public view,
returns a un 256. Turn s underscore players dot length.
We're also probably going to want the latest timestamp. So
we'll do function get latest timestamp. public view returns
you in 256. And we're just going to return s underscore last
timestamp. And maybe we'll want to do request confirmation. So
we'll do function GET request confirmations. Public pure since
request confirmations is also a constant function returns you
went to you went to 56 Turn request c
onfirmations. All
right, we've got some wonderful getters here. Some views slash
pure functions, we have a way to get a random number we have a
way in a decentralized context. Automatic automatically execute.
picking a random winner we have a way for people to enter our
raffle to enter this lottery. And we have a bullet proof way
to solve creating a truly fair decentralized lottery. Oh my
goodness. Let's do one more compile for good measure H H
compile. And these are just warnings. So we're good
to go
here. Our code is compiling successfully. Like I said
Normally, this definitely isn't going to be the way that you're
going to write your smart contracts, it's almost
impossible to write a full smart contract without making any
mistakes. And without flipping back and forth between
documentation, I have already written this contract many times
myself, and I still made a whole bunch of mistakes. So it is
totally reasonable and totally rational for anybody and
everybody to make mistakes goin
g through this. And to use
resources and to write tests along the way. Now that we have
our raffle dot Seoul created, it's time to add everything
else. So we're going to come over here, we're gonna create a
new folder, and add our deploy folder per usual. And we're going to do exactly
what we've already done a couple of times, we're going to create
some scripts to deploy our raffle contract. Now with our
raffle contract, there's a couple of things in here that we
want to make note of first thing
is that our constructor right
now is absolutely massive. There are a ton of parameters in here
that we need to account for. Let's take a look at our
constructors and see if there's any contracts that we're already
interacting with. Okay, VRF, coordinator v2. This is a
contract address, entrance fee, no gasline, no subscription ID
no callback, guestimate No, and interval. No. So knowing that
this is an address should be a tip that Ah, okay, we're
probably going to need to deploy some mocks. For
this, since
we're going to need to interact with a VRF coordinator contract
that's outside of our project. But let's go ahead and start
working on our raffle deployment script first, and we know we're
going to have to deploy some mocks. So we'll just keep that
in mind. So let's create a new file. Oh, one, deploy raffle.js.
And let's get started deploying our raffle contract. Now, this
is going to look really similar to what we've done before. And
we're going to do it again here. If you want to u
se your previous
deploy scripts as a reference, I absolutely recommend you do so.
But let's get started with module that exports equals an
async function. That's going to take get named accounts and
deployments. As input parameters, then we're going to
do const. Deploy log equals deployments, then we're gonna
say const, Deployer, equals await, get named accounts. Let's
go to our config and update module that exports to to have
this I'm just going to copy paste, so that employer is going
to be de
faulted to account zero and player is going to be
defaulted to account one, if you want to go ahead and write this
out, feel free to pause and write out your name accounts.
Right now, there's gonna be a lot of boilerplate in our heart
hat.config.js. So feel free to have the GitHub repo for this
lesson up with you or your previous scripts that you've
already written as a reference named accounts Deployer. And
we're also going to have a player named account so that we
can separate different users
or different players who are
interacting with our contracts. But for now, we're gonna grab
our Deployer. And we're gonna get started. Now similar to last
time, we would just do const, raffle equals await ploy,
raffle, comma, and then add all of our stuff in here, right? So
this would be from Deployer. args, we're going to have a ton
of args. So we're going to come back to this. And then log is
going to be true. And then we're going to have wait
confirmations, there's a little bit more boilerplat
e we need to
work with here in our Hardhead config, we don't have a network
here. So let's add our network information. So we can get those
block confirmations will be specific in here, as well. And
we'll say default network is going to be hard hat. And then
we'll say networks, and we'll add our
network information that's going to be working with heart hub,
which has a chain ID of 31337 miles, we'll put that in here as
well. And block confirmations were just set to one lot this
column here, we'r
e also gonna be running some staging tests on
the Rinkeby network. So we'll add rink B in here with a chain
ID of four, block confirmations of six, and we need to add a URL
and then also some accounts for our URL. We've done this 100
times. We'll do const. rinky dink could be RPC URL equals
process study and v dot rake, the RPC URL, that's private key,
blah, blah, blah, we're going to add all these same variables
from our last projects. So I'm going to ask you to pause here
and just copy paste a
ll those variables from our last project.
Boom, like so since running rink, prpc, URL, private key
corn market cap and ether scan, we're also going to want to make
sure you folder, excuse me, new file, dot env. And we're going
to drop all of our information in here our rink, prpc, URL,
private key, ether scan API key, and then our coin market cap API
key as well. Now that we have our private key or ring prpc URL
down in URL, B RPC URL, and for accounts, we're just going to
add that single privat
e key. Now for weight confirmations is
going to equal For network config dot block con formations,
or one, we're gonna have to import network from Hardhead,
which looks like my VS code automatically did for me. Thanks
VS code. So this is how we're going to deploy a raffle.
Obviously, we have a ton of arguments that we need to
account for. So let's get to it. Let's look at our raffle
constructor to see what we need to get. Okay, well, the first
thing that we need to get via of coordinator v2, we'
re gonna use
the same strategy we used in our Funmi project with using mocks,
if we're on a development chain, and using the actual contract
address if we're on a test net, or a live network, so let's get
to it. So let's go ahead, recreate that helper, hard hat
config dot j s and create that const. Network. Config, say it
equals for hard hat, we're going to use a mock. So we don't need
to put that in here for now. But for rink B, let's go ahead, but
a foreign here, so the name is going to be Rin
keby. And we're
going to need to go to the chain link documentation, the VRF
contracts, and we're going to need to grab the RF coordinator
for the Rinkeby. Test net, we're going to grab this address here,
plop it in here, we'll say V RF core did an eight tour v two,
bam, right like that. So back in our deploy raffle, we're gonna
have to pick whether or not to use the V RF coordinator v two
in the network config or some mock that we deployed, which of
course, leads us to us having to deploy a moc
k, let's create the
new file 00. Deploy marks.js. So same thing module that exports
equals async function where it's taking, get named accounts, and
deployments, as its input variables from the heart at
runtime environment, or do const deploy COMM A log equals
deployments. And then const, Deployer equals await, get named
accounts, Excel, and then we're going to grab the chain ID as
well as we're going to only wants to deploy this on a
development chain. So we'll do const chain ID equals network
dot config dot chain ID. Now we're going to only want to
deploy mocks, if we're on a development chain. So once
again, we're going to go to our helper config, we're going to
add those development chains in here. We'll say const,
development chains equals hard hat, and localhost. And then
we're gonna want to export both of these. So module, dot
exports, equals network config, and development chance. Now in
our deploy mocks, we're gonna want to grab those are saying
const development chains, equal
s require dot dot slash helper
helper hardhat config. Now we can check to see if development
chains dot includes the network dot name that we're currently
on. If we're in a development chain, we're gonna go ahead and
log local network, the protected, deploying box. And
now we'll have to deploy a mock VRF coordinator, where do we get
a mock VRF to coordinator Well, let's go ahead and create one of
those. We go to the chainlink. GitHub
again, we go to contracts. So C 0.8, actually have a mocks
fol
der with VF coordinator v2 Mach dot Sol, and we're just
going to use this as our mock. So in our contracts folder,
we're gonna create a new file called test new file called V RF
core, in a core, the two mock dot Sol, and we're just going to
import this mock and have it be our mock. So we'll do spdx.
We'll do pragma, solidity, carrot zero, point 8.0, or seven
or whatever we want to do, we'll do import at chainlink slash
contracts slash SRC slash v 0.8. Slash, mocks, slash VRF. Pour
did a tour B t
o mock that soul. And we'll just check to see if
it compiles with Hardhead compile, and it looks like it's
compiling as well. Awesome. So now that we have our mock
contract, we can actually go ahead and deploy it. So we'll do
a weight loi, the RF core de ne Tor V to Mach comma, and then we'll give it
our parameters in here. We'll say from Deployer. Log is going
to be true, and then we're going to do our arguments. Now, what
are the arguments of this VRF coordinator, v2 Mk. Well, if we
open the V
RF coordinator v2 Mock, right in our VS code, or
on GitHub, we can actually see We will roll over to the
constructor that it takes two things. It takes a base fee and
a gas price link. What are these first one? Well, the first one
is this const base fee. If we go back to the documentation, we
can see that there's this premium section of 0.25 Link
rink B, this means that for each request, there's a base fee of
0.25 link for every request. So anytime we want to request a
random number on Rinckey,
it's going to cost us 0.25. Link, or
you can think of it as 0.25 Oracle gas to make this request.
So back in our deploy mocks, we can say base V equals, we could
resemble Rinckey here and do 25, blah, blah, blah, or we could do
ethers dot utils dot parse eath, of 0.25. And I'll even put a
little comment here, say 0.25 is the premium, it costs 0.25 Link
per request. And remember, the reason that this cost 0.25 Link
per request versus the price feeds didn't cost anything is
because the price feeds
. If we look back at a chain that link,
each one of these price feeds is being sponsored by a group of
protocols who are paying for all these requests already, since
there isn't a sponsor for this, we are the only ones requesting
the randomness, we get to be the ones to actually sponsor getting
this random number, then the second thing here is going to be
the gas price link. So let's create another const here to
const. gas price link. But what this is, is actually a
calculated value is a calcula
ted value based on the gas price of
the chain. Here's an example if we were to request a random
number on Aetherium, and the eath price skyrocketed up to
like to like a billion dollars, gas would be incredibly,
incredibly expensive. Now when chain link nodes respond chain
link nodes pay the gas fees, who give us randomness, and do
external execution, the chain link nodes are actually the ones
that pay the gas when returning randomness or executing an
upkeep or etc. If we go to our raffle dot sou
l, and scroll down
to perform upkeep, or fulfill random words, it's actually the
chain link nodes that are calling these two functions and
paying the gas for it, they get paid in Oracle gas to offset
those costs. But if the price of eath, or any native blockchain
skyrocketed the chain that nodes itself to pay the gas fee. So
the chain link nodes have a calculated price have a
calculated variable called the gas price per link, which
fluctuates based off the price of the actual chain, so that
they
never go bankrupt. Basically, the price of a
request changes based off the price of gas for that
blockchain, you can kind of think of this as the link per
gas, if you will, for now we can kind of just set it to whatever
we want. And we'll just set it to one e nine, which is going to
be equivalent to 1123456789. So now that we have the base fee,
and the gas price link, we'll grab this base fee, we'll have
these be the arguments for our VR chord, enter V to mock, so
we'll say and actually we can
delete that will say const, args
equals base fee, and gas price link, then we can take this args
variable and just plop it in here. Now we can do log mocks
deployed, then we can do log that a little line like this to
let people know that this deploy script is done, then we'll just
do a module that exports dot tags equals all and mocks. So
now that we have a view of coordinator v2 Mock deployed,
we'll come back over to our raffle and make some code around
it. Similar to what we just did with our
deploy mocks. We can
say if development chains that includes network dot name, we'll
do some stuff. And we need to import development chains from
our helper Hardhead config. And we need to import network from
hard hat. My VS code automatically added them Wow,
thanks VS code. Let's even just do const args and make this
variable down here. Stick it in args. Our first argument is
going to need to be this Vera fi to coordinator so let's make a
variable we'll say let VRF core need torby to address an
d if
we're on a development chain, we're going to grab that mock
contract. So we'll say const PRF, Cor de ATAR V to mock
equals await ethers dot get contract, the RF cord the cord
to NATO Tor v two MK, and then we can set the RF coordinator v
two address equals VRF core denater V two mach dot address. Cool. We have that
address here. Else if we're not on a local network, the VRF v2
coordinator address is simply going to be derived from our
network config. So let's import the network config as we
ll from
our helper Hardhead config and we'll say else VRF coordinator
v2 address equals network config of our chain ID. Let's which
actually sorry, we do need the chain ID cons chain ID equals
network dot config dot chain ID, chain ID of VR coordinator V to
chain ID. You're a coordinator v2. Alright, perfect. We've got
the setup to work with our VRF coordinator v2 address. What
else do we need from our raffle? Well, we need an entrance fee,
we probably want to change the entrance fee depending o
n what
chain we're on, we're on a more expensive chain, we might want
to make this higher than others. So let's go ahead back to our
helper Hardhead config and make an entrance fee based off of the
blockchain, so for NP, maybe we want to make it 0.01 eath. So we
could say ethers dot utils, parse ether of 0.01. And once
again, thank you VS code for automatically dropping that in
for me. And we're also going to want an entrance fee for our
heart app, we can also set a default in here, but let's ju
st
be a little more explicit. So we'll say 31337. The name of
this is Art app, we don't need to give it a veneer of
coordinator v2 address because we're going to deploy a mock.
But we do want an entrance fee. And let's just give it the exact
same entrance fee here. So we'll say ethers dot utils dot parse
ether 0.01 E. Alright, great. So on our deploy here, we can just
say const entrance fee equals network config. Chain ID of
entrance fee. Let's start populating our arcs here. So the
first one is
going to be your Fe two coordinator address. Next
one is going to be our entrance fee. Got it? Got it. Now we need
our gas lane. On Rigby and other networks, there are different
gas lanes that we can choose from, let's grab the only gas
Lane from Rinckey, the 30, gray key hash, let's drop this, of
course into our network config as gasoline, pop it in here,
we're hard hat, our mock actually doesn't care what gas
line we're working on, because we're going to be mocking the
gasoline anyways. So we
can just say gas lane, we can just go
ahead and use the same one or really anything here, it doesn't
really matter. Now here we'll say const, gasoline equals
network config. Chain ID, gasoline, and we'll
grab the gas lane. And we'll stick it into our argument
array, we've got this one, we've got this one, we've got this
one. Now it's time for the subscription ID. Now if you
haven't run through docstoc chain link for the chain link
VRF, I highly recommend you do so so that you can understand
wha
t this subscription ID is we know that we can actually make a
subscription ID using that front end using that website VRF dot
chain dot link, which is great and all but what if we're on all
local chains, we can get a subscription ID no problem in
here. But it's a little bit harder on a local network. Now
I'm actually going to teach you how to create and fund
subscription it is completely programmatically. So you don't
even need to use the UI if you don't want to. However, for the
purpose of this
course, we're still going to use the user
interface, we're still going to use that website for us to get
our own subscription IDs. But you could 100% automate the
process of creating a subscription ID and funding a
subscription ID. Because when you create and fund subscription
IDs, you're just calling create subscription and fund
subscription on that smart contract. So on our development
chain, we have our V RF coordinator v2 Mock, and what
we're going to do and on our development chain, we're
going
to create that subscription. So we're gonna say const,
transaction, response equals await VRF coordinator v2 Mock
dot create sub scription. And then we'll run const transaction
receipt equals weight trends action response, dot Wait, wait
one block confirmation. And inside this transaction receipt,
there's actually an event that's emitted with our subscription
that we can get this is another place where emitting events is
incredibly helpful. So in fact, if we open back up a Vera v2
coordina
tor mark, and we look for create subscription, we see
we emit subscription created with the subscription ID, we can
actually get this event emitted from our transaction receipt.
Now to assign it, let's go ahead and create a sub subscription ID
up here. And then we'll say subscription ID equals
transaction receipt that events of zero dot args dot sub ID. And
again, be sure to watch that events video if you want to
learn more about how to work with events in hardware. Now
that we have a subscripti
on, we need to fund the subscription on
a real network, you'd need the link token to actually fund the
subscription. The current iteration of the mock allows you
to fund a subscription without the link token. So what we can
do is we can just run a wait your F corps inator Mark, if
coordinator B to Mach dot fund subscription, and we'll give it
the subscription ID and we'll need to do some fundamental
this, we can just create some variable, we'll say const VRF,
subscription fund amount equals ethe
rs dot utils, dot parse
ether, of, we'll say 30 VRF subscription, fundament. We'll
just paste that down here. We could do this as well for a real
test net or live networks. But just so that we become familiar
with the user interface, we're not going to do a test net
programmatically. And for a test net, we're just going to use
exactly what we've been doing so far. Or we can put a
subscription ID and our helper config. So we'll say
subscription ID, we'll put something in here right now,
we'll jus
t leave it as zero. But later on, when we actually
create a subscription, we'll update our subscription ID. And
so we'll say subscription ID equals network config. Change ID
of subscription ID. Perfect. Now we can add this to our arguments
array. What else do we need subscription ID, we need a
callback gas limit or callback, gas limit is going to vary
network to network. So once again, we're gonna go into our
helper configure callback gas limit. And for us, we'll set a
pretty high limit of 500,0
00 gas. So we'll say call a gas
limit of 5.123 500,000. Gas. And for hard hat, we'll do the same
thing. So we can say const, callback, gas limit equals
network config, chain ID, callback, gas limit, grab this,
put it into our argument array. What else do we need? All we
need now is the interval. So we can change this network to
network as well. Rigby will say, interval will ever just be 30
seconds for both hard hat and for Rigby. So we'll do the same
thing here. Say const. Interval equals networ
k config.
Chain ID, interval, and we'll grab this, pop it in the end of
our array. All right, awesome. Now we have an argument array
and drop it right in here and perfect. Everything in our
constructor for our raffle contract. Great. This is looking
fantastic. We've got wait confirmations we got logging
arguments Deployer. Okay, well, what next? Well, let's go ahead
and add that verification piece. So once again, create a new
folder utils, new file verify.js. We can either copy
paste this from o
ur last project, or we can grab this
from the GitHub repo associated with this course, once we have
our verify script in here, we're going to import it by saying
const. Verify equals require dot dot slash helper Hardhead
config. And then we can add that same bit of code down here to
verify our contract, we'll say if we're not on a development
chain. And we have process studying v dot ether scan API
key, then we're going to log verifying dot dot and then await
verify raffle dot address and the ar
cs. Now we'll just do a
log of a whole bunch of hyphens say that this script is done
module dot exports dot tags equals say this will be all and
raffle. All right, so let's test this out. We'll do hardhat
deploy, or yarn, hard hat deploy and see if this script works
correctly. Looks like we ran into an error network config not
defined. So let's spell this right. Network config. There we
go. And that looks much better. Let's try this again. It looks
like our deploy scripts are working well. local
networks
protected, deploying mocks deployed VRF coordinator v2 Mock
mocks deployed and then we went ahead and deployed our raffle.
Awesome, this is massive. Now we're not going to test the
deploying this to a test net quite yet, because well, we
don't have any unit tests yet. We need to write unit tests
before we want to ever test running this on a test net. So
we have our deploy script, we have our contracts, that means
it's time for us to write some tests. We'll come over here
we'll create a
new folder called test. And for now, we'll just
make our unit tests. So unit tests, and in here we create a
new file called raffle.test.js. And let's write some unit tests.
Now for these unit tests, we are going to be a little bit verbose
here, we're going to make our coverage really, really good
here. It's not gonna be perfect, but this is going to be pretty
verbose. So I'm gonna go pretty quickly here. So you can feel
free to pause, slow me down, speed me up whatever you need to
learn this se
ction. It is really good muscle memory to go through
writing these tests and understand what you should be
thinking about when you're writing these tests. So feel
free to speed up the parts You already know and slow down the
new parts. Because we are going to go over some new information
here. writing tests may seem like a tedious process. But I
promise as you get better at writing these tests, you'll
realize that these are the things that you can rely on when
stuff doesn't work. And when you're
not sure how to code,
something, getting this muscle memory down, writing these tests
is going to make you a fantastic engineer. So let's go through
and we'll write some of these verbose tests here to try to
make this really good and have this have really good coverage.
And if you want to go back later on, and see if you can give it
even more coverage, and even better tests, please feel free
to do so. But let's get started. Let's write some tests. So we're
going to start out pretty much the sam
e way we've been starting
everything out, we're going to grab our development chains, so
that we only run our unit tests on a development chain. So we'll
do const development chains, equals require dot dot slash dot
dot slash helper, RD, hard hat config, and then we'll say not
development chains, that includes network dot name, and
describe that skip. Otherwise, we'll do describe, so this first
describe is going to be our raffle unit tests. And this is
going to be an async. function. So raffle u
nit tests so that it
comes on the next line, it looks a little bit better. Alright,
great. Now, what are some of the main things that we're going to
need to deploy? Well, we're gonna need to deploy a raffle,
we're probably going to want a VRF core to a tour be to mock
graded before each, that's going to be an async function, where
we go ahead and we get these will say const. Employer equals
await, get named accounts. So we're gonna need to import
get named accounts or require get name accounts f
rom hard hat,
then we're going to want to deploy these using our fixtures.
So we can say await deployments. And then we're going to import
deployments as well from hardhat dot fixture, and we're going to
call all, we're going to deploy everything. And again, if we
look at our one, our raffle has the alt tag, and our Oh, our
mocks also have the alt tag, okay, perfect. Once we deploy
everything, we can say raffle equals await ethers dot get to
contract. And we got to import ethers from hard hat, l
ike so.
And we'll say it will get the raffle contract and we'll
connect it to our Deployer. And then we're going to do the same
thing with VRF coordinator v2 Mock equals await ethers dot get
contract, the RF coordinator, B to MK connect this to deployer
as well. Alright, great, our first set of tests describe,
they're going to be the constructor. And this is going
to be an async function. And let's do this. Let's create an
IT initial initializes the raffle correctly, this is going
to be an async
function. Now I just want to make a note,
because ideally, we make our tests have just one assert, or
it just keep that in mind is that ideally, we want to have
just one assert for it. But we're going to have a bunch
because like I said, we're being a little bit loose here. So we
want to just make sure that our raffle is initialized correctly.
So we'll say const raffle state, and we'll get that raffle state
because we want to make sure that we start in an open raffle
state. So we'll say const,
raffle state equals await raffle
dot get raffle state. And then we want to say assert dot equal
ope and then we need to import assert from Chai. So assert
equals require Chai assert dot equal raffle state.to string
because again, raffle state is going to be a big number. And
even though a raffle state is of type raffle state, it'll return
a zero if it's open and a one if it's calculating. So this gets
transformed just into a un 256. When we call it like this, a
raffle state variable here will be
a big number. So we want to
just to string a file. So assert dot equal raffle state DOT two
string zero. We'll also make sure our interval gets set
correctly. So we'll do const interval equals await raffle dot
get interval. And I don't know if we have one of those it's
actually C inter ball see if we have one of those. We don't have
a good interval let's go ahead and add again interval function
so we'll do function get in trouble. We have public view
returns you went to six return I underscore
interval will have
good interval will say raffle dot get interval and will also
say assert dot equal interval.to string. It should equal one Ever
is in our helper config, right? Because we're using the interval
and helper config. So we say interval.to string should equal.
So we'll import that as well. Network config. And we'll say
the interval should equal network config. Of let's also
make, get our chain ID up here, chain ID, say const. Chain ID
equals network dot config, a chain ID, network co
nfig of the
chain ID of interval. Alright, cool. So let's test this so far,
HH test or yarn, hard hat test. And cool looks like it passed.
And we have our little gas output here. Awesome. Let's go
to our Hardhead config, just so that it doesn't always print out
at gas, but there for now. So I'm going to copy paste the gas
reporter section from our last project, like so. And we're
going to have enabled gas be false for now. So now if we run
a Hardhead test, again, we shouldn't have that gas bid
p
rinted out, we should just see the tests and perfect, that's
what we see. And our constructor test passes. Yay, what's next,
got our constructor. And we probably could have written more
tests for the rest of these. But let's just move on. Alright,
enter raffle, that's going to be our next describe block. So it
will do describe, enter, raffle. And this is going
to be an async function. And we'll say it reverts when you
don't pay enough, right, because one of the first things that we
check is that
they're paying enough. So we want to make sure
that this actually reverts if they don't pay enough. So this
will be an async function. Where we're going to do that same
expect await thing. So we're going to import expect from
Chai, which comes from those waffle matchers. And we're gonna
say await, expect raffle dot enter a raffle. And we're not
going to pass any value here, we're going to expect it to dot
b dot reverted with. And if we look here, we want it to be
reverted with this raffle not e
nough eath entered. So we can
put that in quotes raffle not enough eath entered. Now we can
try this out. Make sure that it actually works. Ah ah test dash
dash grep. Put this in quotes that you don't pay enough. And
awesome. We're passing there. What else do we want to test?
Well, we want to test that if the raffle isn't open, we also
revert but we'll test that in a little bit. As we kind of test
the rest of the functionality. We want to see that it records
players when they enter. So this will
be an async function. And
now we'll enter the raffle. First we're going to need that
raffle entrance fee. Let's go ahead and save that at the top.
So we'll say let raffle VRF coordinator be to mock raffle
entrance fee and then our before each will say raffle entrance
fee equals a weight ethers dot get entrance fee. This should be
raffle dot get entrance fee. So now we have this raffle entrance
fee, you can use it to enter the raffle we'll say a weight raffle
dot enter raffle with a value of raf
fle entrance fee. And we can
make sure that our Deployer here has been correctly recorded. So
since right now we're connected to the Deployer. We'll just make
sure that that deployer actually is in our contract. So we'll say
const player from contract equals await raffle dot get
player of zero, because we record them in our players array
and we have our get players function which pulls them out.
And then we'll say assert dot equal player, player from
contract should be the Deployer. So now we ca
n grep for this in
our hard head test. Make sure this works. HH test dash dash
grep. Deployer is not defined. We've got it up here. But we
actually didn't save it globally. So we got to do let
common Deployer. And we'll say deployer equals await get named
accounts. And we'll wrap it like this to get the deployer
Awesome. Let's try this again. And awesome. What else should
this do? Well, it's also emitting an event. So let's make
sure it emits an event. So we'll say it emits event on enter.
This
will be an async function as well. And this will be the
first time that we're testing to make sure a function emits an
event and the syntax is going to look really similar to what we
test for when we check to see if an error is fired. So we're
gonna say await expect raffle dot enter raffle with value of
raffle entrance fee dot two dot omit and this.to dot emit we get
from During woful, these chime matches for emitting events we
can do a wait expect to emit, and then the event that we're
expectin
g to emit. So we're saying to omit the raffle
contract to emit a raffle enter event. We can copy this, try to
test this in our terminal. So we'll say hard hat. Test dash
dash grep. Just put this in quotes. And that passes as well.
Great. Let's now go ahead and test to make sure that we can't
enter the raffle whenever this raffle is not open or it's
calculating. So we'll say it doesn't allow entrance, when
raffle is Cal cumulating. And this will be an async function.
And first we'll enter will sa
y await raffle dot enter raffle value is raffle entrance fee.
Now what we want to do we want to get this raffle into a closed
state. So we want to get it out of its open state. Well, how do
we move this raffle from raffle dot open to raffle dot closed in
perform upkeep, remove the raffle from raffle that open to
raffle dot calculating but perform upkeep can only be
called if check upkeep returns true. Otherwise, it'll revert
with raffle upkeep not needed. So what we need to do is we need
to make
check upkeep return true. And we will pretend to be
the channeling keeper network to keep calling checkup keep
waiting for it to be true. And once we make it true, then we'll
pretend to be the channeling keepers and call perform upkeep
to put this contract in a state of calculating. Now how do we
actually do that? Well, in order for checkup keep to be true, we
first need to see that we are indeed open, which we are. The
next thing that we need to do though, is we need to do this
time passed bit
. We need to actually wait that 30 seconds
for time to pass. Now, that kind of sounds awful. Do we have to
wait 30 seconds for all of our tests? What if our interval was
10 days when we have to wait 10 days to run our tests? It sounds
ridiculous. Well, hard hat actually comes built in with a
ton of functions for us to manipulate our blockchain to do
literally whatever we want it to do. In the hard hat
documentation. There's a section called Hard Hat Network
reference inside the Hard Hat Network
section. And in here,
there's a ton of information about how the hard hat network
actually works and different configs that we can do with it.
If we scroll down low enough, we can see the JSON RPC methods
that we can use on this blockchain, we can do eath
accounts block number call chinetti. We can do all these
RPC methods that a normal blockchain has. Additionally, we
can do even more than that, we can use these things called Hard
Hat Network methods. Since this is our local Hard Hat Network.
A
nd we're using this for testing, we want to be able to
test any scenario. And it's these methods that give us the
ability to do that, you can go through this and play around and
see all the different things you can do. One of them in
particular is going to be set storage set where you can set
storage at any place, which is really fun. But some of the
special testing debugging methods are going to be EVM
increase time, and EVM. Mine increased time allows us to
automatically increase the time of o
ur blockchain and EVM. Mine
allows us to mine or create new blocks, because if we increase
the time, it doesn't do anything unless there's a new block
mined. So what we can do is we can run a wait network, and
we'll import network from Hard Hat Network await network dot
provider that send EVM in crease time, comma, we can send a list
of parameters to send with it, which for us are just going to
be our interval.to number. And then we'll do plus one. Now
right now, our interval isn't stored global
ly, so we're
probably going to want to do that. So let's go ahead and do
interval. And then in our before each, we'll do interval equals
await, raffle dot raffle dot get interval, I'm just going to copy
this and we're going to delete this whole line. Wait raffle dot
get interval. Since now we're just going to call it interval
at a global level, since we're going to use it a lot. Now we're
gonna say interval that to number plus one. So we want to
increase the time by whatever our interval is to m
ake sure
that we can actually get that checkup keep to return true. So
additionally, we're going to want to do a weight network dot
provider. Dot send EVM mine with an empty array just because we
just want to mine it one extra block. You can also do network
dot provider that request with an await here, these two would
be basically the same, but this one's a little quicker, right?
So we've increased the time of our blockchain. We've mined a
block to move forward. It should be open time has passed
. Do we
have a player? We do indeed, because we've entered the
raffle. We should have a balance because we've entered the right
Apple, check upkeep should now return true. So we should be
able to call perform upkeep and pretend to be a chain lock
keeper. So we're going to pretend to be a chain the keeper
and call await raffle dot perform upkeep. And we're going
to pass this some empty call data just by passing a blank
array like that. And now this should be in a calculating
state. So now that it
's in a calculating state, we can say
our enter raffle reverts correctly if the raffle isn't
open. So now we'll say await, expect raffle dot enter, raffle.
And we'll send it value of raffle entrance fee. We're
expecting this.to dot b dot reverted with that raffle
underscore underscore not open error. Okay, let's try just this
in our tests now. So we'll run Hardhead test dash dash grep.
Put this in quotes. And perfect that passes as well. Now if we run Hardhead test,
let's just test everything to
gether right now. And
everything is passing. Oh, this is wonderful. Awesome. Let's
keep it going. And if we run Hardhead coverage, we'll see our
coverage is bumping up, we are already drastically better than
where we were before. Let's keep going well, let's go ahead and
test our check upkeep now. So we'll do describe, check upkeep.
And this will be an async function, where we'll say it returns
false. If people have been sent an E, E, it's been a sync
function. So we'll have everything in here
b
e true except for the fact that nobody's entered yet. So we'll
do a weight network dot provider dot send EVM increase time,
comma interval.to number plus one will do a weight network dot
provider. That send UVM mine. No parameters. Now we're going to
call check upkeep. Now here's the thing, check. upkeep is a
public function. So if we just run await, raffle
dot check upkeep, and we pass nothing in this is going to kick
off a transaction, because hard had knows Oh, okay, it's a
public function, t
hey're clearly trying to send a transaction
here. If this was a public view function, it wouldn't, it would
return that view. But the thing is, I don't really want to send
a transaction. But I want to simulate sending this
transaction and seeing what this upkeep needed would return.
Well, I can actually get that by using something called call
static, I can simulate calling this transaction and seeing what
it will respond. So instead of raffle dot, check upkeep, I can
do raffle dot cost static, t
hat check upkeep. And this will give
me the return of upkeep needed and the bytes perform data, I
can extrapolate just the upkeep needed out of this return or
writing const upkeep needed equals this, and then I can do
assert not upkeep needed, because right now upkeep needed
to return false. So we'll say assert, not false, which is
true. If upkeep needed was true, then this would be false. And
this would break. So that's what we want to do. So let's go
ahead, we'll run this, see if it worked. Ou
r hat test, dash dash
grep. And we're in passing, we are in business. Awesome. So
this is working perfectly. Well, and let's also test that it
returns false. If raffle isn't open. This will be an async
function. And we'll do everything except and we'll do
everything in here. But we'll make the raffle in the
calculating state. So do a weight, raffle dot enter, raffle
value, raffle entrance fee await network dot provider that send
and I'm just going to copy these two lines here. Because we're
goin
g to go ahead and do those. We're also going to do a weight,
raffle dot perform upkeep. And another way to send a blank
bytes object is to do a string like 0x hardhat is smart enough
to know that this should be transformed into just kind of a
blank bytes object. So either one of these should work. Now
we're do const, raffle state equals await, raffle dot get
raffle state and we'll Get upkeep needed. So we'll say
const. upkeep needed, we'll do exactly what we did above raffle
dot call static dot
check upkeep. Let's say excuse me a
weight, raffle dot call static. Now we can do assert dot equal,
say raffle state is going to be, or excuse me raffle state.to
string, it's going to be calculating, and I started dot
equal upkeep needed, it's going to be false. Let's run a GREP on
that. And perfect. That's also working correctly. Great. Now
I'm going to skip over these next two tests, because we
haven't really learned anything from them. So I'm just going to
copy paste them from the GitHub, we'
re going to return false if
enough time hasn't passed. And we're going to return true. If
enough time has passed, we have players Eve and is open. So
we're just asserting true down here. And we're starting not
true up here, you want to pause the video and copy paste these
and write these out, you absolutely can copy paste them
from the GitHub repo, you absolutely can't like I said,
going through this and making yourself write these and making
yourself understand these tests is going to make you
a
substantially better coder. And let's just test that it all
looks good with hh test. Now, as I was recording this, I just
realized that for all of our describe blocks, I've been
making them async functions describe blocks actually don't
realize and can't recognize and can't work with promises. So
having the be async actually doesn't do anything. So in your
describe block, we want to get rid of the async word because
it's actually not helping us at all. In fact, it's just an extra
word, and it
looks kind of gross. So now all of our
describe blocks, we're gonna get rid of that async keyword, and
just have them be functions. Of course, all of our it's, though,
are going to be using asynchronous functions, which is
what we want. So we've written some tests for a checkup keep.
Now let's go ahead to perform upkeep. Let's create a new describe,
block, ascribe or perform upkeep. This is going to be a
regular function. And in here, we're going to start and say it
can only run if check upkeep
is true. And this will be an async
function. Because we only want to perform keep to work if
checkup keep is indeed true. So we'll say await raffle dot
enter. raffle we'll send it some value, raffle entrance fee, I'm
sorry, there should be curly braces instead. And we'll do a
weight network dot provider that send EVM increase time inter
vol.to number plus one await network dot provider that send
UVM mine an empty array there to the reason that we're moving
time forward and moving our block forwa
rd, of course is
gonna be the same thing as above, we want our check upkeep
to return true. And then we're gonna say const X or transaction
equals await raffel dot perform upkeep. We can either do a blank
array, or we could do 0x Doesn't matter. And we can assert the X.
Now if TX doesn't work, or this error is out or something, this
will fail. Right. So that's how we know that this actually can
work. So let's test this out. We want this only to work if
checkup keep is true. And we made checkup k
eep true by all
the stuff that we did above. So now we'll do yarn hardhat test
dash dash grep with our IT block here. And I spelled perform
upkeep wrong perform upkeep. Let's try spelling things
correctly. And let's run that test again. And great. That's
working. I will What else do we want to do? We want it to revert
with raffel. upkeep not needed if checkup keep is false. So in
here we're gonna say it reverts. When check up keep is false.
This will be an async function. And we're going to do t
hat same
syntax await expect raffle dot perform upkeep empty bytes
object that to.be reverted with. And what do we want it to be
reverted with? Or hoping it's reverted with this with that, we
can run this test here Hardhead test dash dash grep paste that
in. And we see that is indeed passing now something that
you'll notice here is that our revert actually goes ahead and
reverts with all this extra stuff as well. Our test is smart
enough to know that if all we do is put the name of the error
tha
t is getting reverted with then it's good enough. If we
want to be super specific, we can actually go ahead and make
this a string interpolation and add all of these in here. So we
can add the balance that we expect we can add the players
that we expect. And we can add the raffle statement for now
we're just going to keep it as we're expecting this but if you
want to be super specific you can have your tests expect for
exactly the specific values that you're looking for. But alright,
what is the
last thing we should expect form? Well, we should
check to see that this actually gets called the raffle state
gets changed. And we admit this event. So let's go ahead and add
that. We'll say it updates the raffle state admits an event and
calls the VRF coordinator to be an async function as well. Let's
do this. So let's go ahead and let's make checkup keep true,
I'm just going to copy paste these first three lines, since
it's going to be exactly the same. We're going to enter the
raffle we're
going to increase the time we're going to mind a
new block, then we're going to call perform upkeep. So we're
going to say const, TX response equals await raffle dot perform
upkeep with an empty bytes object, and we're going to do
const TX receipt equals await TX response dot wait for one block.
From this receipt, we're gonna get the request ID, we're gonna
say const Request ID equals, we can get the request ID. From
this omitted event. However, we should look at our VRF
coordinator mock again,
when we call request random words, both
in the mock and then in the actual contract, you'll notice
that it also emits an event with random words requested. And if
you look in here, the second parameter that it has is indeed
the request ID. So in reality, US omitting the request ID is
redundant, we can just use the admitted Request ID from the VRF
coordinator for the purpose of this course and showing you what
an event looks like. We're going to leave it in there. But if you
want to go back and r
efactor this, you would definitely want
to remove this omit. But for this test, let's do TX receipt
dot events. And this is going to be the first event instead of
the zero with event because before this event gets emitted,
this function is going to emit an event. So instead of the zero
with event, this is the first event that gets emitted after
this one. So tax receipts dot events of one dot args dot
request, ID, and then we're gonna say cert request id.to
number is greater than zero. And then w
e'll also assert that the raffle state equals equals one.
So we're gonna do const, raffle state equals await, raffle dot
get raffle state. And this should actually be raffle
state.to Number, or to string and then you know, do whatever
we want to do. And this is a very big it, but we're going to
copy the whole thing anyways. HH test, dash dash grep, paste that
in there, we run it, excuse me.to string equals equals one,
transformer time and perfect, we are passing Great, now it's time
for fulfill
random words. And this is where we're going to
learn a lot of fantastic stuff here. So we're gonna make a new
describe block zoomed out a little bit here. And this is
going to be our fulfill random words. It's going to be a
function, of course. And in here, we're actually going to
add another before each want to have somebody have entered the
raffle before we run any tests in here. So we're going to do a
before each, which will be an async. function. And we're just
going to run await, raffle tha
t enter raffle with a value of
raffle entrance fee. And then we're going to do a wait network
dot provider dot send EVM increase time of interval, that
to number plus one, and then a wait network dot provider dot
send EVM mine. Comma. Before we've tried to do any testing of
or fulfilled random words, we're gonna have somebody enter the
lottery. And we're going to have increased the time and mined a
new block. Okay, cool. So the first thing we want to do is
want to see that fulfill random words c
an only be called so long
as there's a request in flight, so long as there's a requested
as long as request random words has been called. So we can
actually check that by running it can only be called after
perform. This will be an async function. And in here, we're
going to revert on some requests that don't exist. So we'll do
await, expect the RF core didn't a tour be to mock dot fulfill
random words. And if we look at our V RF, coordinator v2 Mark in
here has the fulfill random words function
, which is what
the chain link node actually calls and inside Add this
function in the actual contract calls another contract that does
the random number verification. So we're basically checking this
part right here. If the request doesn't exist, we're going to
get this non existent request here. And as you can see, it
needs a request ID and a consumer address. So we're going
to guess zero. And the consumer addresses, of course, it's gonna
be raffled out at address, we're going to expect this t
o be
reverted, with non existent request. And then we're going to
do this exact same thing with a different Request ID or request
ID one. And hopefully, we're also going to get non existent
requests. Now ideally, no request here would ever allow
this fulfill random words to go through. Now, it obviously would
be really hard for us to test every single possible Request
ID, we're going to see a way in the future to actually test for
a ton of these variables with something called fuzz testing.
But
we'll get to that in the future. And I spelt the scribe
wrong, let's let's actually spelled the scribe correctly, go
ahead and run this hh test dash dash grep. And great it passed. Now I'm
going to make just one more test here, that's gonna be way too
big. But right now the test that we're about to write is going to
be it's going to be a really big test. And we probably want to
split it up into different sections, but actually figured
that this was actually the best way to show this section. And
it's going to be exactly what we're going to do. When we get
to our staging test, we're going to write this test literally
almost exactly the same. So let's write it, this is
basically going to be the test that puts everything together.
So we're going to test that this indeed, so winner resets the
lottery, and sends money, which is kind of a lot for a single
it, we probably would want to split those into their own
pieces. But for this, we're just going to put them all into one,
and it's going t
o be an async function. Now we are going to
learn a couple of new tricks here. So definitely be sure to
follow along. Now for this one, we're also going to add in some
additional entrances additional people who are entering this
lottery. So we'll say const, additional, and trance equals
great. We're gonna have some more of those fake accounts from
ethers enter our lottery here. So we're gonna say const.
Starting account, index equals to, since deployer equals zero,
excuse me equals one, since th
e player is zero, so we're going
to have new accounts start from index one. And we're going to do
a little for loop or let i equals starting count index, i
is less than the starting count, index, plus additional and trend
sees there's gonna be entrance. i plus plus, we're gonna do a
little loop and connect our raffle contract to these new
accounts. And then we're going to have these new accounts,
enter our raffle contest. Count, connected raffle equals raffle
dot connect accounts of i and do we
have accounts defined
somewhere we don't. So let's get accounts to find somewhere.
We'll say const accounts equals await ethers dot get signers.
And then we're going to do a weight account connected raffle
dot enter. raffle with a value of course of raffle entrance
fee, we're going to connect three
additional entrance to our raffle. So we're going to have a
total of four people connect into this raffle. Now that we
have them in here, we're going to keep note of our starting
timestamp. So we're g
onna do const starting timestamp equals
await, raffle dot get last timestamp. And here's where
we're going to get a little bit tricky. What we want to do is a
couple of things we want to we want to perform upkeep, which is
going to mock being chainlink keepers, which will kick off the
chain link, which will kick off calling fulfill random words.
And we're going to mock doing that as well. Mock being the
chain link VRF. Once we do that, we can of course just check to
see okay, did the recent winn
er get recorded the raffle get
reset just play as we said as the timestamp is everything
reset. But we want to do this in a specific way. If we're doing
this on a test net after we call fulfill random words, we will
have to wait for the fulfill random words to be called. Now
since we're working with a hardhat local chain, we don't
really need to wait for anything, right because we can
just say okay, boom, snap our fingers and adjust our
blockchain to do whatever we want. But we're going to
simul
ate that we do need to wait for that event to be called. So
in order for us to simulate waiting for that event, we once
again need to set up a listener. Now if we set up a listener we
don't want this to Just to finish before the listener has
is done listening, so we need to once again create a new promise.
And this is going to be incredibly important, especially
for our staging tests. So we're going to do await, new promise.
And this is going to be exactly the same as we set it up before,
it's g
oing to be an async function that's going to take
resolve and reject as parameters. And we're going to
use this little arrow syntax here saying, this is an async
function, basically, and we're going to set up once again, that
once syntax, we're gonna say raffle that once, what's the
event name, winner picked. So we're gonna say, Listen, for
this winner picked event, we're gonna say raffle dot once winner
picks happens, do some stuff. And again, this is just an
anonymous function. So we're gonna
say raffle dot once the
winner picked event gets emitted, do some stuff. So we're
setting this up. Now it's in this function, we're going to
add all of our certs and everything because we want to
wait for winter to get picked. Now before the event gets fired,
though, we of course, need to actually call perform and call
fulfill random words. So this is going to seem like it's a little
bit backwards. But that's because we want to set up our
listeners so that when we do fire, the methods that will
fire
the event, our listener is activated and is waiting for it.
So we're going to put all of our code inside of this promise.
Now, because we put it outside of the promise, we put all the
code outside of the promise, this promise will never get
resolved, because the listener will never fire into vent. So if
down here, you know we call fulfill random words with
something, you know which the spelling is bad. But let's say
we call the down here, this piece of code will never reach
this fulfill ran
dom words, because it's always going to be
waiting for this wants to get resolved. So we need to add all
of our code inside the promise. But outside this raffle dot once
now we don't want to wait forever, right? Maybe there is
an issue here. And we want to be able to reject this, if there's
an issue. Now what we can do is in our heart hat dot config, we
can add a timeout. So we can add this mocha section, we can give
ourselves a timeout of 200,000 milliseconds, which is going to
be 200 seconds m
ax, if this event doesn't get fired in 200
seconds, this will be considered a failure and this test will
fail, which is what we want. And I typically like to just wrap
this in a try catch because if something fails, it'll cause you
a whole bunch of headache catch E. And if anything fails, we'll
also reject, there's an issue with us calling some function,
we'll just say, Hey, okay, that's a failure. But you fail,
that way, our promise can get resolved in a timely manner,
we're going to add this c
ode in a little bit. But let's keep
going. Let's keep going. And excuse me, the trycatch should
be in the ones, the ones above the results, excuse me, because
this is the listener. So sorry, we want the trycatch to be
inside the ones if this takes too long, we want to just go
ahead and throw an error. Otherwise, we're going to
resolve now outside the listener, but inside of the
promise, we're going to do this bit here, where we go const TX
equals await raffle dot perform upkeep, and we'll pass i
t the
empty bytes object, we'll get const TX receipt equals await TX
dot weight of one block. And then we're going to do a
weight VRF core didn't a tour be to mock dot fulfill random
words, CX receipt dot events of one dot args dot Request ID come
a raffle that address. So then the final thing that we're going
to do is we're going to get this veer off coordinator be to mock
we're going to have it call fulfill random words, which
takes the request ID and the consumer address. So we're going
to mo
ck it, give it the request ID which we get from the
transaction receipt, and the consumer address here. All
inside this promise, we're setting up a listener for this
winner picked event. And then we're mocking the channeling
keepers and then we're mocking the chainlink VRF. And once this
function gets called this function should emit a winner
picked event. So this raffle that was set up that was
listening for this to get emitted will pick up and go Ah
okay, I found it I found the winner picked e
vent. Now we can
go ahead and do some stuff. So once the winner picked event
gets fired, we'll do a little console dot log found the event
like this and we'll jump into our try catch and this trycatch
is going to be basically us doing all these asserts in here.
So first we want to say const recent winner equals await
raffle dot get recent winner and we're going to be checking just
everything in this raffle right we're gonna be checking that the
recent winners right that that the raffle state's b
een reset
the players have reversed, you know, players has been reset,
etc, etc. So we'll say const raffle state equals awaits
raffle dot get raffle state we'll say const. Ending
timestamp equals await, raffle dot get last timestamp. And
let's start doing some asserts. So first we should assert that
this es players array has been reset to zero. So if we call get
number of players it should be zero. So we can do const num
players equals await raffle dot get number of players like so.
And we can d
o assert dot equal nine players.to string is going
to be zero. What else can we assert? Well, we can assert dot
equal raffle state should be back to being open. So raffle
state.to string should be zero, we should assert that the ending
timestamp is now greater than the starting timestamp. Because
the last timestamp should have been updated. We also want to
make sure our recent winner is correct. But we'll do console
dot log recent winner. Now we can go to the VRF coordinator
mock. And we could s
imulate this and try to figure out who the
random winner is to console dot log accounts to. And then just
to show a bunch of them, we'll do 01 and three a raffle dot
once this needs to be an async function, not just a regular
function. Let's try that one more time, get latest timestamp
is the correct function. So let's update this with the
correct latest. Let's put latest in there. Because I'm spelling
something's wrong. I sure am looks like we're printing out
recent winner here when we haven't
even initialized it. So
let's move it up. And then we'll do console dot log recent
winner. So I know this is a massive test here. But let's
give it a try and see if everything kind of does what we
think it should do. And it's slowing down here, which is good
because we're doing a lot of stuff. And uh huh, we finally
get this passing thing to come out. And we did a ton of console
dot logging so so there's a lot of stuff in here of the signers
are getting printed out. So let's just make this a lit
tle
easier to read. We'll add dot address to all these, we can see
who the winner is. So it looks like the winner is going to be
account number one, which is great. So what we can do now
that we know account number one is going to be the winner, we
can get that winners starting balance way down here before we
call fulfill random words. So we'll say const winner, starting
balance equals await accounts one dot get balance, yes, you
can just call get balance right like that. And now that we have
th
e winner starting balance back in our tests, we can say const
winner, ending balance equals await ounce one dot get balance.
And we can make sure that this winner got paid what they need.
So now we're going to do a big assert with some money stuff,
just trust me, this is what the math is. So we're going to do
assert dot equal winner balance.to string, excuse me winter ending
balance.to string should equal the winner starting balance dot
add the raffle entrance fee that multiplied by the addition
al and
trance that add the raffle entrance fee that we paid.to
string. So this math is basically saying the winner
should end with a balance of all of the money that everybody else
added to this contract. And that's it. So we can run this
test one more time. And Gosh, darn it, it passed. Okay, so
there was a lot of code here. And this might have been one of
the hardest pieces of this entire course is going to be
this part right here. So if you struggled a little bit with
this, don't let that bog
you down. This is probably one of
the more difficult sections of this course, let's do a quick
refresher of just this test that we're doing. And then we'll see
in an action when we do it. In our staging test. What we did is
we're picking a winner resetting the lottery and we're sending
money. Basically what we're doing is we're testing that this
fulfill random words thing does what we want it to do a random
winner wins and they get the money. So how do we actually do
that? Well, we first starte
d off by having a bunch of random
people enter the lottery. Great. Sounds good. Now what we wanted
to do was we want to call perform upkeep and fulfill
random words, we want to pretend that the random number was
drawn. And that's what this code down here does is it calls that
random number but and what we could have done was we could
have had all these assert and checked all the variables. After
we did this, right. We could have totally done that. However,
on a test net where we don't always kno
w exactly when a
transaction is going to finish. We have to wait If we have to
listen for an event to be fired, before, we could call the
transactions that would end this whole thing, we needed to set
something up to listen for that event to be fired. And we said,
hey, only once this event is fired only once this transaction
is called, can we do our testing now for our local network, we
are mocking the VRF coordinators, we have control,
we know exactly when this is going to run. But on a test ne
t,
we don't. So you'll see in our staging tests, we won't have any
of this here. And we'll have to rely on setting up a listener to
listen for the channeling VRF and the keepers to fire their
events. And that's why the staging test is going to be so
important to make sure that we're doing everything correct.
And that's why we set up our local tests like this, so that
it mimics what we're going to be doing on our statement test what
we're going to be doing on a real network here. And again,
we're
setting up this listener and we're saying, Ah, once we do
here, this event, then we're going to try to actually check
all of the balances and check that everything is working as
intended. And if we don't see it, we're going to reject. And
if there's a timeout, if it takes more than 200 seconds,
we're going to say okay, something went wrong, we're
going to cancel it. And actually, I'm going to bump this
up to 300 seconds, because I think 200 seconds is not going
to be enough. And depending on ho
w quick the rink B test that
is, you might have to bump this up even bigger. So just keep
that in mind. But who, okay, we have just built some fantastic
tests. Let's go ahead, and let's just run h h test to see if all
of our tests are going to pass. And well, 14 passing, everything
is passing here we are looking good. This is fantastic. Alright, so
now that we have our unit tests, let's go ahead and create a
staging test. Our staging test is going to look really similar
to that massive test that
we just created down here. And the
reason we set up our unit test to do this a weight promise
thing with the raffle dot once was because this is actually how
we're going to need to wait on a test net or main net for a
winner to be picked. We cannot on an actual test net, pretend
to be the chain link V REF, we can pretend to be the chain link
keepers if we want. But we're not going to make sure that the
10 the keepers is actually working. But we are going to be
doing this because we want to list
en for that event to be
fired, we want to listen for the chain link VRF to respond with
the winner. So let's create a staging test. And this is a test
that we're going to run on an actual test net here. Okay, so
we're going to create a new test called raffle dot staging dot
test dot Jas and this is where we're going to put our staging
test. Now we can actually code this pretty quickly because most
of our staging tests is gonna look real similar to our raffle
test here. So for now, let's just gra
b this whole first part,
and then we'll adjust it as we need. And then we'll close it
off. Because we're definitely gonna need a raffle, we're not
going to need a VRF coordinator mock because again, we're not
going to be using a mock. Since we're on an actual test net, we
will need the raffle entrance fee, we will need Deployer, we
will need a Deployer. And we might need the interval. But
let's delete it for now we probably won't need the chain
ID. So let's delete that as well. Awesome. Somethin
g that we
want to keep in mind is that when it comes to our staging
tests, we only want our steam test to run when we're on a
test. Net, we don't need to run our unit tests because our unit
tests aren't checking that compatibility with a test net,
we want our unit tests to only run on a local network. And we
want our staging tests to only run on a test network. This is
where again, in our test, we're going to check to make sure what
type of chain we're on. And oops, it looks like I already
impor
ted the development changes here. So we're actually in our
staging tests, we're going to check before we run any test
what kind of network we're on. So we're gonna say, if our
development chains that includes network dot name, we're gonna
say if our development chains includes network to name, so if
the chain we're on is in the development chains, and again,
we're going to use this ternary operator where we say, if we're
on a development chain, do something. And then if we're not
on a developmen
t chain, do something else. If we are on a
development chain, what are we going to do? Well want to skip
this and we can actually skip this by putting in this describe
dot skip. And this will skip this whole section here. And
then we can say, if we are the development chain, go ahead and
do our thing. So this is some really nice syntax that allows
us to skip our staging tests if we're on a local network. And
additionally, we can grab this syntax, go into our raffle dot
test dot j s where we have
our unit tests and add the bang
operator, which is the knot and hit Save. And now we're saying
if we're not on a development chain, skip it and only run this
if we aren't in development chain. So this says run this
only on a test net or main net. And then this has run this only
on a local network. Great. So we have a deployer which we're
going to need. We are not going to need to deploy any fixtures
because we're going to run our deploy script and our contracts
should already be deployed. We wi
ll need a raffle we won't need
a VRF coordinator mocks we can delete that We will need the
entrance fee, and we probably won't need the interval. So we
can go ahead and delete that too. Alright, awesome. So we
have our describe, we have our before each, let's make our
tests. And I'm just going to make one giant test to test kind
of everything end to end. And you can add more tests later on
yourself, if you want to our staging test is going to be
really similar to this massive test that we made d
own here. And
in fact, we're going to use most of this code here as our
boilerplate. So let's create a describe. And we'll say, and
we'll actually just copy this describe the four random words
and paste it in here. Because again, we're going to be using a
lot of the same code in our staging tests here. Great. So
now we'll say it works with live chain link keepers and chain
link VRF, we get a random winner, acing function. So this
is going to be our test in here. So in this test, we of course,
we
want to enter the raffle. And we shouldn't have to do anything
else except for enter this raffle. Because the chain the
keepers and the only VRF are going to be the ones to actually
kick off this lottery for us, we'll do a quick grabbing of the
starting timestamp to have it before all this kicks off. So
we'll say const starting timestamp equals await raffle
dot get last to me get latest timestamp, get latest timestamp,
we're gonna grab this because later on, we're going to test to
see if the ti
mestamp has indeed moved forward, we want to enter
the lottery, right, we want to run the command we've been
running over here all the time, we want to do you know await
raffle dot enter raffle, but we don't want to call it yet.
Because same as what we did over here, we want to set up our
listener first. Now in here, we probably should have set up our
listener before we entered the raffle. However, we controlled
the blockchain. So putting it in that order was is was okay. But
we want to set up t
he listener before we enter the raffle just
in case the blockchain moves really fast. And we're going to
set up the listener the exact same way, we did it over here.
So we're gonna say await new promise. And it's going to be an
async function that takes a resolve and a reject. And we're
going to use a little arrow notation here. And in here,
we're going to set up the listener, we're gonna say raffle
dot once, once that winner is picked, we're going to do
another async function using that Arrow.
Arrow function syntax, we'll say
console dot log winner picked the event fired. And only once
we get this winner picked, can we start doing our asserts in
here, can we start making sure that there's a winner, there's a
verifiably random winner, it's been picked, the money has been
moved, etc. This is where we'll do our try catch. And if there's
any error, we're just going to automatically reject, we're
going to reject the promise. And if all goes well, of course,
we're going to resolve the promi
se. So our listener has
been set up here. We haven't added our asserts here. But we
will, let's just go ahead and write the rest of the test. And
then we'll go back and we'll update this listener so so our
listener has been added. And inside here is actually where
we're going to enter the raffle. So inside here, await, raffle
dot enter raffle value is going to be raffle entrance fee. And
really that's it. Right? So we're setting up the listener,
setting up the listener, then entering the raffle.
And this
code won't complete until our listener has finished listening
because again, this whole SEC is in a wait. So we're gonna say
okay, cool setup, the listener, wait for this to finish. And
then when it gets here, it goes out okay, this is the end of the
code Are we all done executing Oh, no resolve or reject hasn't
been called yet. And that's because we're still waiting for
the listener to finish listening. Now, once we get this
winner picked event emitted in here, we're going to get that
recent winner so we'll say const recent winner equals await
raffle dot get recent winner will get the raffle state we'll
say const. raffle state equals await raffle dot get raffle
state, we'll get the winners balance. So we'll say const.
Winner balance equals await recent winner. And since we're
only entering with our Deployer we should check to see the
deployers balance at the end and we can't do it right with this
deployer object here. So we'll have to do the player account
equals await ether
s dot get signers, no wrap this actually
actually will just say this is accounts here like that. And
then we'll just do a count of zero because it counts as zero
is going to be our Deployer. So our winner balance is going to
be accounts a zero dot get balance. And then we're going to
do const ending timestamp equals a weight raffle dot get latest
To timestamp, and we should also get the starting balance. So
we'll say winner ending balance, we should also get the starting
balance right after we e
nter. So we'll say const. Winner,
starting balance equals await accounts. Zero dot get balance,
so that now we can do some comparisons. All right, great.
Let's do the comparisons now. So we should first expect the
raffle to be reset. So we can do this a few different ways. Down
here. We did number of players. We can also say, await, expect
raffle dot get player 0.2 dot v dot reverted, right because Get
Player zero should get reverted because there's not even going
to be an object at zero. So tha
t's another way we can check
to see if our players array has been reset. Next, we can do
assert dot equal recent winner.to string, this should
equal our account zero dot address. Okay, our Deployer What
else can we do? We will assert dot equal raffle state to zero.
We want this email to go back to open after we're done. And then
we finally want to make sure that the money has been
transferred correctly. So we'll do assert dot equal, this should
be a winner ending balance. That to string should b
e equal to
winner starting balance, add raffle entrance fee.to string.
So if we look down here, they enter the raffle we check their
starting balance right after they enter. And they basically
should just get that raffle entrance fee back right because
they are the only ones who have entered this raffle. And then we
can do one more assert, do assert that the ending timestamp
is greater than the starting timestamp. And then we'll of
course, say resolve. So this all goes well, we resolve. If
there
's an issue with any of these asserts, we're going to
catch those errors and we're going to reject and this is
going to be false. And this whole test is gonna go ah, there
was an issue, we now have a staging test that looks really
good here. Let's try this out. Let's try our staging
test out from start to finish. So now in order for us to test
this staging test, from end to end, you first going to need to
get our sub ID for the channeling fear F then we're
going to need to deploy our contract us
ing the sub ID, we're
going to need to register the contract with chain link VRF.
And it's somebody we're going to then need to register it with
chain link keepers. And then of course, we're going to run the
staging tests. So let's do it. So first thing we're going to
need to do is what? Get our sub ID for chain link VRF. Okay,
great. So we're going to come over to VRF dot chain dot link.
And we're going to need to create a new subscription. If we
don't have enough rink B eath. Let's we want to
head over to
the full blockchain solidity course, here, we're going to
scroll down. And we're going to look for the recommended test
and here, which is ranked v. And we're going to use the faucets
link to get some Rigby link, run Fosses dot chain dot link, let's
switch over from COVID to a cerium. Rigby, we know we're
going to need some link and some eath. So let's just go ahead and
get both. Alright, great now that our transaction has gone
through, let's just double check our wallet here. And i
t looks
like we do indeed have Ethereum here. And if you don't see the
link, you can head over to link token contracts, Link token
contracts in the chain link documentation. We'll scroll down
to Rigby, we'll grab this contract address import tokens
and we'll paste it in here. Add Custom tokens, import tokens.
Great. Now I can see my eath and my link here, perfect. We have
some eath we have some link, let's head over to V REF
subscription management. And we're going to create a new
subscription.
Again, we could totally do this
programmatically, because the user interface here is only
helping us facilitate call contracts to the registration
contract that's completely decentralized and on chain. So
let's go ahead and create subscription will create
subscription will confirm the transaction and Metamask on the
Rinkeby network will do a little bit of wading in great once it's
gone through, you can go ahead and click the add funds button.
I'm going to show you what it looks like if you accid
entally
refresh and jump off though. So if you refresh and you go back
to veer off the chain dot link, you should have a new active
subscription. And you'll see this number here. If you click
on it, this is your subscription ID great. So we can actually
take this, come back to our code into our helper Hardhead config.
And we can paste our subscription ID under
subscription ID for our Rinkeby network here. Awesome. Now that
we have a subscription we can see it's not funded with any
link. So we do
n't have any Oracle gas here. And we don't
have any consumers. Right our consumer is going to be a raffle
or a lottery contract. So let's add some funds first and we
don't need to add A whole lot because we're only going to be
testing once. So let's go just go ahead and add to link here,
this number might change depending on different costs of
the test nets and how much link token there's available. So if
you're actually working on a main net, be sure to head over
Doc's touching that link EVM ch
ains contract addresses, you
can read more about the costs some of these different chains,
so you can figure out exactly how much to put in here. And if
you go to the full blockchain solidity course, Jas, we can
scroll down to Lesson Nine, there's a recommended link
amounts or rinky saving tests for chain link VRF. For now
we're going to put two for keepers, we're going to put
eight, but feel free to refer to here so you know how much to put
in. So let's go ahead and confirm, we're going to appr
ove
adding funds here, go ahead and confirm. And we're now funding
our subscription to so we can pay that Oracle gas to get our
random numbers. Great. And once we're funded, we can close it,
we'll do a little refresh. And we can see the balance is now to
link when we don't have any consumers perfect. So we've got
our sub ID, we funded it. Now let's go ahead and deploy our
contract. And we already know that we should be all good for
deploying our contract, we go to our Dotty and V will need to ad
d
all of those same parameters from our previous projects,
we'll need to shrink the RPC URL, we'll need our private key.
If we want to verify we'll need our ether scan API key. And if
we want to do gas output, we'll need our coin market cap API
key. So let's make sure we have all that. And we'll look at our
deploy script. Once again, just real quick look at our helper
hardcat config just real quick, and it looks like we do indeed
have everything in here. And we should just be able to deploy it
i
n one command. So we should be able to do yarn,
hard hat or just h h again, ploy, dash dash network Rinkeby.
Let's go try this out. All right, it looks like we've
compiled successfully, we've deployed it successfully. And
we've even verified it, we can go and open it up on Rinku ether
scan. And we can see our code here has been verified. And it's
looking beautiful. We can read from it, which is great. We can
see all these commands here now that it's verified. And if we
look at the Git raffle sta
te, we should indeed see that it's
open, right, and it's going to stay open until somebody ends
the raffle and updates the amount of eath that the contract
actually has. Now that we've deployed a contract using that
sub ID, we need to register the contract with chain link V REF.
And with chain link keepers. So we need to add this consumer
does tell chinley VRF. Hey, this is the contract that you're
looking for now. So we're gonna go back to V REF dot chain dot
link, and we're going to grab this
contract address, and we're
going to add it as a consumer, your subscription is ready, you
can now add consumers we're gonna add consumer. And again,
this website is just here to help facilitate us interacting
with the contract. So we're going to approve that user,
we'll go ahead and confirm and the transaction is going
through. While we wait for this to go through, we can go to
keepers dot chain dot link, and do the same thing work with the
user interface to register a new upkeep. So we'll go a
head and
add our email Hardhead free code camp@gmail.com. We'll call this
raffle upkeep. We'll paste our upkeep address in here we have
our admin address, and you can ignore this bit right here. For
gas limit, this is going to be the gas limit of the PErforM
upkeep function. If we did our gas estimator, we could just
check to see how much that perform upkeep costs. But for
now, I'm just going to put 500,000 That's probably
overkill, but that's fine. Check data, we're going to keep blank
because
again, our checkup keep doesn't take anything. And then
starting balance, we're going to put as eight. And if you forget
to put a starting balance here, you can always find it later. So
let's go ahead and register, we're gonna get a Metamask pop
up, we're gonna go ahead and hit Confirm. And we can go back to
our VRF and see that it's indeed been added and awesome, we now
have a consumer on our VRF. So now let's just wait for our
keepers to go through upkeep registration requests submitted
succes
sfully on domain net, you might actually have to wait a
little bit for your request to go through, but I'll test that
it should automatically go through. Now if we go back to
keepers dot chain dot link, we should now see we now if we
scroll down to my upkeeps We have a raffle upkeep here and I
have to because I accidentally use the same account that I
tested on, you can ignore the two you'll have one but this is
the one that we just created. And we can actually see what our
balances and then wha
t the minimum balance for this
actually is. So it looks like eight Link was a little bit too
low. So let's go back to Fosses dot chain dot link slash rinky.
Now that we have some more link, we can come back to our raffle
upkeep. And we go ahead and hit add funds. And we'll add just
three. And we'll go ahead and confirm we first need to give
permission to spend. We approved our link transfer now let's
actually transfer the link to the contract and alright funds
added successfully. So now let's do
a little refresh. Now we no
longer see that message saying that it's underfunded and we
have our balance and we can see that it's more than the minimum
balance. We have See the history that we just funded this twice.
Once this actually kicks off, we'll see activity type will be
like perform upkeep or something got our sub ID, we've deployed
the contract, we've registered with chain link V REF, we've
registered it with chain link keepers. Now all we need to do
is run the staging tests. Now runni
ng our staging test is
essentially going to be the same as us calling this enter script,
right, because all we're doing in our staging test is entering
the lottery. And then we just have a whole bunch of validators
that we're running to make sure that things are doing as we
expect, since our contract is actually verified, what we could
do on Rigby ether scan is we can actually go to this right
contract section of the contract. And we could even
connect our wallets to it. And once this turns from
red to
green, after a little refresh, we'll now see that it's green,
it's connected, we can even call functions on this contract
ourself, so we could enter the raffle ourself, we would add,
you know, however much eath to enter the raffle, and that would
kick off the keepers in the VRF, as well. So we could call it via
ether scan, we could obviously call it via our staging tests
here, we could call it via our scripts, we could call it via
the console, there's a ton of ways to actually do this. B
ut
moment of truth here, we're gonna run our staging tests,
which is going to have us enter the lottery, and set up a
listener to make sure that everything works correctly. And
additionally, we'll see on our raffle upkeep history, we'll see
a transaction go through, and then we'll see a transaction on
our chain like VRF as well. Are you ready? I sure am. Let's do this. So we'll do h h,
test, dash dash network, rink B. And that should be all we need
to do. In our state and tests, we probably shou
ld have added
some console dot logs in here to tell us hey, to tell us what
steps that we're on with each. But we forgot to. So if you
follow along with the repo associated with this, we've
added the console dot logs in the test there. But all right,
if we go to the ether scan for this contract, we go back to the
rink the ether scan for this, we paste in that address, the first
step that we're doing in this test, of course, while we're
setting up this listener, the first transaction is going to
be
entering the raffle that's going to kick everything off. Right.
So if we refresh a little bit on ether scan, we do indeed see
we've entered the raffle. And we've updated the balance of the
raffle. Okay, awesome. So raffle has been entered. Now then what
happens? Well, if the raffle has been entered, if we go to raffle
dot soul, if it's open, if enough time has passed, if
there's players and it has a balance, which we just checked
does, this will get kicked off by the keepers. So if we go to
t
he keepers, and we do a little refresh here, after a little
bit, we do indeed see checkup key passed and we see a perform
upkeep having gone through great, what does perform upkeep
to well perform upkeep calls the chainlink VRF. So now if we go
over to chainlink, VRF, we do a refresh here, go down to
history, we do indeed see one of the transactions has gone
through we can see the transaction hash, the link
spent, etc. And now if we go back to our tests, we do indeed
see raffle unit tests for fr
iend fulfill random request winner
pick event fired. And this means that we just went through this
entire process of having a perfectly truly decentralized
raffle work on an actual Testament with our integration
tests working correctly. Absolutely massive, huge
congratulations if you've made it this far. And if you just
walk through the integration test with me now bounce over
contract has now been reset to zero because our wallet address
just won the lottery right and got the money back. Now yo
u
might be wondering, Hey, I see the Create raffle function. And
I see enter raffle but I don't see didn't the chain link nodes
just call perform upkeep and fulfill random words, I'll come
no matter how often I refresh, I don't see those transactions
here. Well, those are actually going to be considered internal
transactions fulfill random words, it's actually called
through the VRF coordinator and the VRF coordinator contract
then calls for full randomness. So we can go to internal
transactions
. And one of these transactions is going to be the
transaction to the VRF coordinator contract, which
calls our contract. Same thing with perform upkeep. Perform
upkeep, the chain link nodes actually call through the
registry contract and then the registry contract calls perform
upkeep. That's what we see here. And if we go through the
internal transactions, we'll see him there. Now let's look at
this enter raffle as well. And since we've learned about events
and logs, we can actually go to the
logs section now. And we can
see our log or event being emitted here we can see the name
raffle, enter, we can see the topic zero which is going to
identify this entire event. And then we also see this number
here which is what which is index topic, one address player
and then there's no data associated with this right
because we only have index parameters which again show up
as topics so this is absolutely phenomenal. Wow, absolutely
massive and we can rerun a unit test Just by hh test in this
will only run our unit tests. And we can see that these are
all passing as well, things are looking fantastic here. And we
have just successfully created a verifiably random, autonomous,
decentralized, raffle and or lottery deployed on the
blockchain, you should be so excited right now. Now, I'm not
going to show you how to push this up to GitHub. However, if
you want to push this up to get up, and again, tweet at me,
tweet at chainlink tweet at Free Code Camp, please feel free to
do so because
you just did an amazing job getting this far.
And if you're gonna want to push this up to GitHub, remember,
we're going to want to put a dot Git ignore. In here, we add a
ton of stuff like that VS code artifacts, cash deployments,
known modules, etc. Type chain types, all this stuff, you can
find a sample dot Git ignore, of course, in the GitHub repo
associated with this, this is an advanced project, we did a lot
of really advanced things here. And this is the section of the
course, where I thin
k at this point, you've got most of the
fundamentals down. And now we're going to move into more front
end. And we're going to move into more industry specific and
more advanced topics that are really going to supercharge you
and make you one of the masters of the blockchain in the smart
contract realm. So huge congratulations, one more time,
definitely go celebrate, definitely go for that walk,
take a quick break, let everything we just learned
settling your brain and get ready for the next one
. Alright, now we're gonna go over
the TypeScript edition of this lesson, we're gonna go a little
bit quickly here, because we're not learning too much new stuff
for this TypeScript edition. So if you want to just follow
along, you can open up the repo here and use the TypeScript
branch. One thing to note that is a little bit different is
when we do our promise in our tests, we're doing a weight
promise void, because we're not going to be returning anything
with our promise here. But the rest is
going to be exactly the
same. We're going to have a hard hat dot config to TypeScript
that's going to use imports. Once again, everything else is
pretty much the same. We're going to export the config like
we did last time. In our package dot JSON. Of course, we're going
to have all of our TypeScript dependencies as well. And then
our deploy scripts are going to follow that same functionality
that we've used before. So now our deploy scripts use a type
deploy function on our variables that we e
xport at the bottom.
And we also import the hard hat runtime environment type, which
where we pull the deployments get named account and networks
from in our tests. Of course, we're still importing the types
of these contracts from type chain slash types, like you see
here, like for example, raffle is going to be signed to raffle
VRF coordinator V to mock is going to be signed to type VRF
coordinator V to MK and if we wanted to deploy with these
contract factories, we could as well. So those are
going to be
the main differences with TypeScript. There's not anything
really new there. But again, if you want to follow along with
TypeScript, and you want to code everything in TypeScript, and
example is here for you All right, welcome back. I hope
your break was absolutely fantastic because now we are
getting into more advanced full stack slash front end
development. We've done the back end development, we've created
smart contracts, we created our lottery. In our last section, we
have all
this wonderful code that allows us to work with our
own provably decentralized lottery. In order for regular
everyday people to use our lottery, we're going to need to
build a front end. Now previously, we learned to build
a front end with raw HTML JavaScript, with our Funmi
project, we created a front end with just pure HTML and
JavaScript, just creating applications with HTML and
JavaScript is great. And if that's what you want to do, you
absolutely can. But doing it with those vanilla protoco
ls has
some limitations. And working with a framework, like what
we're going to be working with in this section is going to make
our lives a lot easier, we're going to be able to develop
quicker, have more functionality, and do more in
less time. As you remember back to the project wasn't really a
fleshed out project, if you remember, it was just a bunch of
buttons. And that was really it. So working with a framework is
really going to enable us to put more features and add more
styling to our a
pplications really easy. Once again, the
code for this entire section is located in our lesson and is
located in this GitHub repository associated with the
course. Now I have to put an asterisk here, as I've said many
times, the front end sections of this course are not required,
we're not going to learn anything new about the back end
in this front end section. So if you don't care about building
websites, all you want to do is learn the smart contract aspect
of this course, you can skip these
sections. But if you do
want to learn how to build these front end applications, if you
do want to give users and non developers the abilities to
interact with our smart contracts, then please continue
to watch. Now I also have to put an asterix here, because if you
haven't worked with some of these frameworks before, the
learning curve can seem a little bit steep. And as I've said many
times, this isn't going to be a front end course that would take
several more hours than what we want to do fo
r this video. So if
you've never done front end before, this is one of the
sections where I do recommend you follow along with one of
these optional sub lessons, we're not going to play them
here. But if you follow along with one of these sub lessons,
that will definitely be incredibly helpful. One of the
videos that I have for the sub lessons is this video right
here, how to connect your smart contracts to Metamask. And it
shows a number of ways connecting your smart contracts
and building kind
of these these front ends. It does start with a
raw HTML and JavaScript edition. So you can really get some more
practice in here with HTML and JavaScript. And then it moves to
index js ethers and a few other next Jas based applications.
Doing it a few different ways will install a new some more
insight on what you should be thinking about when you're
approaching these. So this is absolutely a video to watch if
you're new to front end development. Additionally, for
this section, like I said, we
're going to be using a framework.
And in particular, we're going to be using the next Jas
framework. Now. Next, Jas is a react based framework. React is
a framework for building front end and full stack applications.
Next, Jas is a framework on top of the React framework. So if
you already know react, most of this is going to come very
naturally to the reason that we're using React in next Jas is
because React is easily as of right now one of the most
popular frameworks and languages out there.
And it's no surprise
why we've got a little article in the GitHub repo titled Why
you should use React js for web development, which we'll go into
a little bit more. Some of the biggest applications like
Facebook and Instagram use React js and a number of other Fortune
500 companies and React js especially in the blockchain
space is easily the most popular with protocols like uniswap, and
Avi also using React Jas. Now the reason we're using this next
Jas on top of React Jas is that next Jas, I
think makes working
with react much easier. And I want us to work with the easiest
and most powerful framework out there. Next, Jas is also getting
a ton of steam has some really advanced but in my mind easier
to use features than just raw react. Now, like I said, if
you've never worked with React before, if you've never worked
with next Jas before, and you want to do a little brush up,
definitely watch my video six ways to connect your depth to a
wallet. And if you find yourself struggling with
with this
section, because the front end stuff doesn't really make sense.
We also have a next Jas crash course in the GitHub repo
associated with this course. It's about an hour long and it
is absolutely phenomenal. Free Code Camp also has some next Jas
crash courses. So if you're struggling with the front of it,
go take it next Jas course, and then come back to this section
or skip the front end sections all together, do the rest of the
course of just the back end, and then come back and do th
ese
front end sections. We're going to show you the cutting edge
ways to interact and work with your front end and then also
deploy them. And if you follow along correctly, by the end of
this, you'll have a website deployed that you can show off
and you can send a link of it to your friends. And now I know we
already showed you what this looks like but I'm going to show
you it one more time just so we can walk through and see exactly
what we're doing. So we're going to have this decentralized
lo
ttery where we can go ahead with can connect our wallet if
not already connected. And we can switch, we can switch around
between networks. And our app will actually recognize it and
say, hey, the only supported chains that we're working with
here are going to be 31337, which is localhost, or four,
which is ring feed. And we'll learn how to add this validation
into our application so that our app only works when we're on a
chain that we want, then we can go ahead and interact with it.
Normally,
we can click a button Metamask will pop up, we can go
ahead and confirm we'll get a little transaction notification
saying that transaction is complete. And we'll get our
front end updated. And then on the back end, we'll be able to
see the chain link nodes and the Chinuch V REF do their work. And
once they actually pick a winner, after a refresh, we'll
be able to see that the back end node was updated, we do indeed
have a previous winner. Awesome. Now, not only that, we're going
to show you how
to build this, but also, we're going to show
you how to host it on an actual site. So you'll be able to push
it up to your own blockchain. And additionally, we're gonna
host it in a decentralized context. So this site that we
have here is it's going to be hosted on a technology that
allows us to host websites in decentralized contexts as well
swore backends. And even our front ends can be hosted in
decentralized context. So So I hope you're incredibly excited
for this because we're going to be
showing you the cutting edge
tools that many of the top blockchain projects use. And
let's just jump right into it. Now, I'm currently in the
Hardhead smart contract lottery project, the project that we
just did, we did this hard hat smart contract lottery, which is
great. What we're going to do now is we're going to create a
new folder, although we are going to be coming back to this
folder from time to time to make a couple of updates. So if you
want to keep it open, you absolutely can. But fo
r me, I'm
gonna go ahead and CD download directory to kind of my main
directory for this whole course. And we're going to create a new
directory called next Jas. smart contract. Lottery, FCC, then
we're going to cd into next Jas smart contract lottery FCC, and
go ahead and hit code period. And like I said, if you want to
keep that one up and have this new folder up, you absolutely
can. But basically, again, we're just opening up our VS code in
this next Jas smart contract lottery folder. Now for
this
front end stuff, in particular, if you want to be absolutely
sure you're using the same versions as I am, what you can
do is you can git clone this repo, and then copy the yarn dot
lock and package dot JSON, and then run yarn, this will make
sure you're always using the exact same packages that I'm
using, and you'll never run into any weird issues. So if you do
run into an issue, one of the first things to do, especially
for these front end parts is to go back, make sure you have the
exact
same yarn dot lock and package that JSON that I do in
my examples here, and go from there. Now we're here in our
front end project. And we're going to create a website, we're
gonna create a front end for application, like I said, we're
gonna be using next Jas, if you want to follow along with the
next Jas documentation here, they do have a great getting
started and walking through this. So for us, we're going to
do yarn, create next app, and then put a little period saying
we want our next app
in this directory. If you don't put this
period, it'll create it in a new folder, and you'll have double
folders. Okay, awesome. So and once we do this, in our little
files explorer section, we now can see all the different files
that come boilerplate with this. Now, let me just do a quick
walkthrough of what's going on here. Again, we also go through
this in those two videos that I recommended, but it doesn't hurt
to go over it twice. So node modules, of course, is going to
be the package in th
e installations pages is going to
be the different pages on our site. Let me let me show you
what I mean by this. To run this whole thing. Actually, we can
just run yarn, run Dev, and we'll get started server on
blah, blah, blah, blah, blah, you can command click it or copy
paste it into your browser. And now you'll see on localhost
3000, we have our page up here. Now what we can do, actually, so
index.js is going to be our default page, which is, you
know, considered this slash here. But what w
e can do is we
can create new pages. So I could do new file, you know, dog dot
Jas. And then you know, just copy paste index into dog to
Jas, paste it, delete everything inside the div, you don't have
to follow along here and just go Hi, save this, go back here. I
can now put in dog and get this. So pages is going to be all kind
of these different routes to these different spots on our
website. And index is going to be our default, kind of like our
homepage. So I'm going to delete Dardo. And ins
ide these files is
going to be something called react syntax or JSX. They come
as Jas but they're basically reacts in text. Next, Jas is
based on React, you'll see these pages are this weird combination
of both JavaScript and HTML. We see some import stuff at this at
the top, which reminds us of JavaScript and again, you'll see
some important stuff at the top and then down here, you'll See,
like div head main h1 P, you'll see all these like HTML tags
react index js allow us to do this combinatio
n of JavaScript
and HTML. And it actually makes life a lot easier. Now, you'll
also notice we're doing imports in here. Remember, I told you
all this earlier, imports work with our front end require does
not. So that's some of the difference between node does not
equal JavaScript, right? This is where the differences can start
getting a little bit confusing. But the way that I usually like
to think about it is I just say, no JS, excuse me, I'll just say
back end. I can J S is a little different
from front end. Yes.
So back end, Jas and front end Jas are a little bit different.
That's kind of the way I like to think about it. app.js is going
to be our entry point for everything the way react and
next Jas work is everything is what's called component based in
all of our files here, you're gonna see this export default
function home or something along those lines. What react and next
Jas do is they say, Hey, this huge clump of HTML stuff that
has a hodgepodge of JavaScript inside of it is
considered a
component. And so all of our pages get wrapped through this
underscore app.js page. So this is a page but it's kind of like
the main entry point. And they get stuck into this component
section of our app that Jas so you can think of this on this
graph. The GS is kind of the whole application or whole front
end. And on this homepage, this component, we're sticking index
dot j s right in here, we're swapping out component for index
dot J. S. Now API is what we wanted to do if we want
ed to do
like some HTTP, GET HTTP POST requests, but we're not going to
do any of that. So we're going to pretty much ignore API for
now public is just going to be some public images like a
favicon or Versaille, that SVG styles is going to be the CSS
for our project. CSS stands for Cascading Style Sheets. And it's
basically a way to style your HTML, we're going to change the
way we do styling and a little bit, but that's basically what
these both do the ESLint I'm dumping this right now we have
our dot Git ignore which we know what it does. We have our next
dot config dot J. S. This is a configuration file or next. Jas,
of course, we have our package json, we have a readme, and we
have our yarn dot lock. So most of what we're going to do is
actually going to be inside this Pages folder. And we're also
going to create a couple other folders that are going to be our
main stuff now, because I'm me, and like I said, I love working
with prettier, I'm going to automatically dump a prettier
R
C, and a prettier ignore in here, just so that I can format
my code a little bit nicer, you can grab your prettier RC from
our last projects, you can grab your prettier ignore as well. Or
you can just pause me right now copy, paste them from the GitHub
repo associated with this lesson, paste them in, and then
we'll do yarn, add dash dash Dev, prettier, so that we can
auto format all of our code for come back over to here, we can
save and boom, stuff gets auto formatted. Now, again, we're
going t
o be using the multi terminal feature. So right now I
have one running my front end. So if I come back to the front
end, I hit refresh. It's still running. And then I have one to
do, you know my scripts and stuff. We have yarn run dev
running right now. And if we go to our package, JSON, running
yarn, run dev just runs next Dev. And actually, let me cancel
it and just do yarn dev yarn is actually smart enough that don't
need to do run Dev, but it just runs next Dev. And this next
command comes b
uilt in once we installed next, which we did
when we did yarn create next app. So next Jas comes with
these scripts already built in for us, we want to build our
front end to enable people to interact with our lottery in a
fair way, and connect with the smart contract that's running.
Let's create some simple front end pieces for this. So first
thing we're gonna do is we're gonna go to Pages, and we're
going to go to index.js. All this stuff in here is cute and
nice. And thank you next, Jas, but
we're going to delete it all
and delete everything except for that headpiece, we're gonna
leave that up, we're just going to change the name. But this way
down here, we're going to change the name from Crate next app to
smart contract lottery or raffle or whatever we want to do.
description will be our smart contract, lottery. And then
right below the head, we're going to write hello, blue, and
save. And if we look at our front end, we now see that it
says smart contract lottery and the top and
I'm going to move
this all the way over here, smart contract lottery and the
top and we see Hello, so smart contract lottery. Hello, the
description here, we're not going to see this is going to be
something that web scrapers and stuff we're going to find. Now one of the first things that
we're going to need to do is we're going to need to create
that connect button. We've done this in the past with raw
JavaScript, but now we're going to do it with next js and react.
The one that we made previou
sly was pretty minimalistic. In
fact, we bring it back up it checked to see if there was
windows that Aetherium and then went and requested and connected
and said okay, cool. You're connected. Now what did didn't
do was a lot of things that we would want an application to do.
When we change networks, our application didn't detect that
when we change users or application didn't detect that
it was really stringent in the functionality that it actually
had for connecting to a wallet. So we're going
to make our
wallet connect button, incredibly powerful, so that you
can connect with the button, you can switch networks, you can
switch accounts, you can pretty much do anything in our
application will know our application will be responsive.
So that's gonna be one of the first things that we're going to
do, we're going to create a header and connect button nav
bar one a little navbar here, saying, hey, you know, you can
connect with this button. So that's gonna be the first thing
that we're g
oing to do. Now we can build our whole connect
button in this index dot j s and stick it in here. But instead,
what we're going to do is we're going to make it a what's called
a component. So we're going to create a new folder called
components. And we're going to create a new file in here called
header.js. And you might also see a lot of people do
header.js x.js and.js x do literally the exact same thing,
you can do either one, I'm going to do dot JSX, just to remind me
that this is a React fil
e that we're creating. This is a
component that we're creating. But yeah, you can do J S JSX. If
you want to learn a little bit more about components, we've got
a link to learning about components. They're basically
independent and reusable bits of code. They serve the same
purpose as JavaScript functions, but work in isolation and return
HTML. So basically, we're going to create like a little chunk of
HTML that we're going to export into our index such as like what
we've done in the past, this
just helps modularize and reuse
this header component, you know, across our project. Now, we're
only going to be using our header in one area. However,
it's still nice to modularize the project regardless. And to
get start, this is going to be what's called a functional base
component. So we're going to create basically a function
called home write really pretty much exactly like what we see in
JavaScript, except it's going to return some HTML. So we can do
like a little div. And my VS code auto
created the closing
div here. And in here, I'm going to be like hi from header. This
is going to be a real minimalistic component like this
is a valid component here. Now we have this function that
returns HTML. And to give other applications, the ability to use
this component will do export default function home. And then
in our index.js, we can import it with imports. Home actually,
excuse me, I'm not going to call it home, we're going to call it
header, header, excuse me, export default func
tion header,
and then import header from the down directory components slash
header, like so. So now we've imported our header in index
such as if we go back to our front end, which is still
running, we don't see it in here, right? Remember,
everything goes through our app. And when we're on the slash
page, that's going to go to our index.js. index js is importing
our header, but it's not returning our header, right, we
see. And here, we see it returns. And this is the HTML
that it's returning.
And as you can see, there's clearly no
header in here. So now that we've imported our header, we
need to actually add our header in here. So we'll do header, and
then adds the closing tag right here. If you don't add any
stuff, you know, in between two tags, you can go ahead and just
do this one liner here, with a backslash at the end saying,
Hey, this is an open and closed tag here. Now that we've
imported it, what do you think we'll see on the front end, now
that we've added it to our index js
, you're right, we see hi from
header, because we added our header here. And then we see
Hello. So high from header, hello, boom. Now, anything that
we do, obviously now in our header.js, will see reflected on
our front end. So we can do hi from header, blah, blah, blah,
you know, just a bunch of dribbles, and we'll see it on
our front end. So this is going to be our section where we're
going to make our header or a navbar or all the functionality
for the Connect button. Now I'm going to tell yo
u something, and
some of you are going to love this summer, you're going to
hate this. I'm going to show you. I'm going to show you the
hard way first, then the easy way. Why am I going to show you
the hard way first? Well, because I want you to become
familiar with what's actually going on behind the scenes and
what's actually going on with some of these components because
it is really helpful when building these front ends to
understand, Okay, here's what's going on. If you don't
understand wh
at's going on behind the scenes, you're gonna
go to try to build more advanced applications, and you're gonna
have no idea what to do because you've just learned the
shortcut. I like to think of it as like calculus, like we're
going to learn how to calculate a derivative first, and then
we're going to learn the shortcut to quickly getting
derivatives. So don't skip this part, because this is going to
help you well and beyond down the line. Okay, we're going to
learn this the harder way to set ev
erything up in our HTML Funmi.
We just use raw ethers to kind of do everything and you
absolutely can use raw ethers to do everything. However, there
are some packages especially for React that make life developing
a front end substantially better and in our full blockchain
solidity course, Jas. We have a number of other packages that
you can use will stack development and other libraries.
And if you watch this six ways to connect your depth to a
wallet, you'll actually understand some of the
di
fferences. So if you haven't watched that video, go back,
watch that video. But there's a whole bunch of libraries that we
can use that are going to make our lives a lot easier. We've
listed some of them here. React Morales is the one that we're
going to be using today, they have some additional plugins,
and they have probably my favorite thing on the planet,
which I'm going to show you how to do very soon. But these are
also open source, Ross also comes with some optional
functionality to hook
into your own back end to give your app
even more features and even more functionality. And that's the
other reason that we're doing it. So and we're gonna go over
that later. And if you want to use pure ethers, you absolutely
still can. A lot of these packages that we're using do
rely on ethers, but we're not going to use just ether. So we
go to the React Morales page. And to get started, we can just
do this bit right here. So we're literally going to copy this and
bring it into our project. An
d if you go to our package that
JSON, we actually already have react and react DOM. So we can
just do yarn, add Morales, and react oralis. Now you'll notice
I'm not doing these as dev dependencies, I didn't do yarn,
add dash dash dev here. The reason is, because for our
production builds, when we actually create the website
here, you will need Morales and you will need react Morales we
don't need prettier to create a website prettier is a tool that
we're using as developers. So in all of our pro
jects, so far,
we've been using just dev dependencies. That's because
we've only been building our projects for developers, our
GitHub repos, they haven't been made to build a website, they've
only been to do things on the back end, for our website, we're
actually going to be building a front end. So we need to put
this in the dependencies section, because we need to say,
Hey, these are the ones that we need to bundle up together for
the front end, and you can ignore these ones. And if it's
just
like a tool to make our lives better, it's gonna go in
dev dependencies. So we're adding Morales and react
Morales. And a lot of the syntax that we're going to do for our
header is actually going to be really similar to what we've
been seeing so far. So let's do this. And then actually, we're
just going to change this name to manual header, manual header
update imports for manual header, you can go ahead and hit
yes to man you all header here, we're gonna copy manual header,
make sure it's in o
ur index.js it we're gonna change header to
manual header, and we're gonna change how to manual header
here. And the reason we're doing this is because like I said,
we're going to create a much simpler header after we create
this kind of harder one. And we're going to want to create
that connect button, which again, we made in HTML Funmi. By
calling eath request accounts, what we can do actually with
Morales is we can just do this thing called Enable Web three.
So at the top, we're going to impo
rt, use mirallas. From react ralis. And if you go
to the React Morales page here or to their GitHub, you'll learn
how to set all this up too and learn more about the
documentation. So we're going to import use Morales from react
Morales, and inside our function here, but outside of our return,
we're going to say const, Enable Web three equals use mirallas.
Now use Morales is what's known as a hook as a React hook. And
it's a way to keep track of state in our application. Now,
in order to use mir
allas, our entire application needs to be
wrapped around what's called a Morales provider, which is going
to be a context provider for us. And I'll explain what that means
in a minute. But basically, what we need to do is we need to add
this Morales provider to our app.js. So in here, we're going
to import oralis provider from react oralis. And we're going to
wrap our entire app around this Morales provided so we're going
to do some little parentheses here. New Line, we're going to
paste morass
provider like this, it's going to give us the
closing tag, copy it, paste it like this and save. And then in
here, we're going to write in the sheet allies on Mount equals
false. This initialize on Mount piece here is the optionality to
hook into a server to add some more features to our website. We
don't want to hook into a server for this application, we want
everything just to be open source, and we don't need any of
this additional functionality. So we're just going to do
initialize on Mount
equals false. Now that the whole thing
is wrapped in this Morales provider, we go to the front end
we should be able to refresh everything looks pretty much the
same. And we can start using these hooks. Now this use
Morales is what's known as a hook and hooks can be a little
bit confusing to understand at first glance, but they're
incredibly powerful and they are the de facto way for us to build
react projects. And if you're familiar with class components,
we're not gonna be using class compone
nts because hooks are
much better hooks allow function components to have access to
state and other react feature ers state being probably one of
the biggest ones and the most popular ones, we want our
application to be different if we're connected to Metamask,
versus if we're not, right, if we go back, if we go back to our
example website here, right, if we're not connected, we want to
say, Please connect to a wallet. And then when we are connected,
we want to go ahead and be connected. If I ha
ve, let's say,
I have some variable like, and I don't have this hook here, I
have like, let connected equals false, right, or enable Web
three, or is web three enabled, let's say I've led connected
equals false. And then I have, you know, let's say have some
button that connects us and changes connected to be true,
changing connected to be true is great and all but it won't
rerender our application, you see, when I disconnect and I
reconnect here, our application actually changes based off of
wh
ether or not we're connected. And this is what we want, we
want our front end to rerender. When we're connected, if I just
use a variable like this inside of our component, our front end
isn't going to rerender. Or even worse, if I use it outside, our
component doesn't even know anything about this changing. So
hooks are a way for us to actually work with state
especially and automatically rerender when something changes
and enable Web three is going to be a function that we get from
this use Mo
rales hook to do that. So for a lot of our
components, instead of just saying like, let web three
enabled equals true, like we did in normal JavaScript, we're
going to be doing a lot of this, these hooks. For the most part,
we usually want our website to change based off of if some
variable has changed and enabled web three is a function that we
get from this hook that says, Okay, go ahead and connect
Enable Web three is basically the equivalent of saying try
await Aetherium dot request, like th
is. Now enable Web three,
the way we're going to use it here only works on Metamask. But
we will show you how to get this kind of this cool little module
up where we can choose between different ways to connect our
app. We have our enabled web three,
let's go ahead and create a button, that's going to do the
same as what we did in our HTML Funmi in our return, but here,
instead of hi friend header, we're going to add a new
component, or we're going to add a new tag, we're gonna add the
button ta
g, and for me it automatically close to, and I'm
just gonna call it connect, I'm gonna go ahead and save. Now we
see a little button that says connect. And obviously, it
doesn't do anything, we're gonna give this some functionality,
we're gonna say on click now since again, this is a JSX
component. This isn't raw HTML, in raw HTML, we can't just kind
of stick JavaScript wherever we want. But in JSX files, we can
stick JavaScript kind of wherever we want. So inside of
this, inside of this block o
f HTML, we can actually stick
JavaScript in here by adding these little brackets. So adding
these little brackets in our jobs can return. But here, we
can add JavaScript. And what we want to do is we want to have
our On Click Call Enable Web three, we're going to call an
async function, we're going to use the arrow syntax here, one
click, we're gonna call this async function, which is just
going to be await, Enable Web three, await Enable Web three,
let's add the little parentheses here. And ess
entially, with just
this, we've done pretty much everything that we had back in
this big connect function here. Now if we go back to our front
end, do a little refresh, we can see that right now, we can see
that I'm actually connected, I'm gonna go ahead and disconnect.
I'm still connected from some of the last applications I was
doing, we can hit Connect, and now we see metamath does indeed
pop up, we'll hit Next connect, and boom. And that's all we need
to do. Now, if we look at our Metamask,
it says connected.
Great. Okay, cool. So now we have a way to actually connect
here, let's add some functionality and make our
application smart enough to have the connect button if we're
connected. And if we're not connected, not have that button.
So what we'll do now is use Morales comes with another hook
called is web three, enabled, which is just a variable part of
our hook that keeps track of whether or not our Metamask is
connected. But we can actually do one better, we can actually
check
to see if there's an account because maybe web three
is connected. But but they didn't connect it to an account.
So let's go ahead and we'll import count from us. Morales
will check to see if there's an account. So what we'll do is
inside of our div tags, we'll do a little JavaScript, we'll do
that ternary operator again, we'll put a count here with a
question mark. And we'll do that that same syntax that we've seen
before, we'll say account, if account exists, do this. If
there's no account, do
this. And if there's no account, we want
to add this Connect button. So we'll go ahead and we'll stick
this in here. If there is an account, we'll just show that
account, right. So we'll do div backslash div in here, we'll
just say connected, like that. Now if we go back to our front
end, we see connected. If we do a little refresh, it'll go away.
So we'll we'll re hit Connect, and now we're connected, which
is great. Let's make it even smarter. We'll have it show our
account here. So instead o
f just saying connected, we'll say
connected to I'll put some JavaScript, we'll put some
JavaScript inside of our JavaScript. But since this is
that HTML stuff, we got to use these brackets to say JavaScript
again. So we'll put JavaScript inside of the JavaScript, we'll
say connected to, we can just say account. Now go back to our
front end, we see connected to blah, blah, blah, blah, blah,
you'll notice if you hit refresh, you'll have to
reconnect, we'll get to that in a minute, what a lot of p
eople
do is they'll do a count dot slice, zero, comma six. And then
outside of the brackets, they'll do dot that dot, another set of
brackets account, that slice, count dot length minus four,
close that off like that, we have our account, when we hit
Connect, now it says connected to blah, blah, blah, hello. And
because of these hooks, when I switch accounts, and I connect
with these new accounts, it even automatically re renders
switching accounts. So these hooks are really good for re
renderin
g our websites whenever some value changes. For example,
if I said like, let count number equals seven, and we had a
button, that updated account number, our front end wouldn't
rerender unless we told it specifically to rerender, which
gets really annoying. So hooks kind of help us a lot with doing
that. And it allows us to keep track of states between renders. Now, here's something that right
now it doesn't do, if I hit refresh, I have to re hit this
connect button, even though my Metamask says
, Hey, we're
connected. But if I refresh, I have to re hit this Connect
button. Why is this happening. Or when I hit refresh, our
website doesn't know that we've hit enable Web three already,
right, because we basically go back to blank when I refresh,
and then I have to hit Connect, which is really obnoxious and
really annoying. So we want to add some functionality, so that
automatically the instant we render, we go ahead and we check
to see if we're already connected. And if we're
connected to
show this. Now to do that, we can use another hook
called use effect. And this is a core react hook. So we'll do
import use effect from react like this. This is a core hook
directly from react. And it's one of the most popular out
there along with use state. And we've left some links to
learning more about the effect hook in the GitHub repo
associated with this course, I'm gonna give you my summary of
basically what this use effect does, we basically have this
function called use effect, which
takes two parameters, it
takes a function as its first parameter, and then second, it
optionally takes a dependency array. And what this use effect
is going to do is it's going to keep checking the values in this
dependency array. And if anything in this dependency rate
changes, it's going to call some function, and then rerender, the
front end. So for example, using Morales comes with this function
called is web three enabled or this variable called is what
three enabled. If we add this to our
dependency array, what we
can do in our use effect is do console dot log, hi. And then we
can do console dot log is web three enabled, what this use
effect is going to be doing is it's going to constantly be
running, this is running all the time. And it's going to be
listening to see if is web three enabled changes, right? And
anytime we run Enable Web three is web three enabled becomes
true. So so now if we go to the front end, we do a little
refresh, we see high false, high false. Now why do w
e see this
twice is web three enabled only changed once? Well, this is
because of how use effect works, it will automatically run on
load or right or the first time it does and then it'll run
checking the value. So we're basically seeing this run twice,
it runs the first time we load it, and then it'll check the
value and run again. So we see go twice, even though it's
really just once. But if we go back here, sorry, let me just do
a quick reload again. We hit Connect, we now see Hi is now
true,
because it saw is what three enabled change to true
because enabled web three made is what enabled return true. And
it ran this again, right. So that's how that actually works.
And there's a couple of different ways to think about
this. Actually, we actually don't even need to give this an
array. And what happens if we don't give this an array? Well,
let's refresh. We'll hit Connect, and we'll see it's
still ran a couple of times. So if we don't give it array, no
dependency array, it'll run any
time something re renders. And
you need to be careful with this. Because then you can get
circular renders, if you have some use effect that changes
some value. And you have another use effect that re renders when
that value changes while they're both just going to keep changing
back and back and forth. So no dependency array like this. It
will run anytime something re renders we'll give it a blank
dependency array. It'll just run once on load, just run one time.
So now like we have a blank dep
endency right in here, we
reload. Right? We see that it runs twice. That's actually
because we're basically re rendering once in the
background. So it really is just running Once but there's
something else going on in the background. So it looks like
it's running twice. Now if we just add is web three enabled,
do a little refresh, it'll do the exact same thing, it'll run
the same amount as if this was blank. But when we connect,
it'll add here. If this was a blank array, when we refresh,
we'll s
ee kick out twice, which should be ones. But like I said,
there's something going on in the background. If we hit
connect. Now, we don't see anything here, because the blank
dependency array says, Hey, I'm only going to run one time on
load. Now, if there's our stuff in this array, like is web three
enabled, it's going to run anytime something in this array
changes, right. So again, we'll refresh will connect, we see it
ran again, after a hit Connect will refresh. Hi, hi connected,
ran one more
time. So so that's kind of the cheat sheet here. If
we give it no dependency array, it's going to run any time
anything in this project re renders, if we give it a blank
dependency array, it's just going to run one time, or like
we saw, it ran twice. But that's because there's something else
we render in the background. And if we give it dependencies in
this array, it's going to run anytime something in this array
changes. And this is really helpful, because oftentimes,
we're going to want our f
ront ends to rerender, this use
effect will say, oh, cool, some value changed, I'm going to run
this function, and then I'm going to rerender, your front
end. And now we're going to use this
use effect thing to make sure that when we refresh, it
remembers that we're actually connected. So how do we do that
inside here, we are going to use this is web three enabled thing.
And the first thing that we want to just do is we want to say if
is what three enabled, then we'll just return. Because if
we'
re already connected to web three, then we don't need to do
anything. Now, if we're not connected to web three, and we
don't have an account, we'll want to go ahead and call Enable
Web three, right want to automatically enable Web three.
So now if I go back, you'll see with just this code, I'm just
always automatically calling Enable Web three. But this can
get really annoying. Because if I disconnect, right, let's go
disconnect everything. Now refresh, it's going to always
call Enable Web three
, right, every time we refresh, it's
going to automatically call enabled through without us even
hitting the Connect button. So that's no good to we want to
actually see if we're connected. So the way we do this, like I
said, we want to use our local storage, again, application, or
these little, this little thing here go to Application, we want
our we want our application to remember that somebody hit this
connect button, and they went and connected to us. So what
we're going to do is in our lit
tle onClick function down
here, we're not just going to call await Enable Web three,
we're also going to store a little remembrance here saying,
Hey, we actually did connect recently. So below this, we're
going to run window, dot local storage, dot set item,
connected, comma injected. I'm actually going to comment this
out for now, because my friend is just going to keep popping up
like that. So what this does is we're saying, okay, in our
window, because again, if you go back to the window, you
go to
console, a type window, you're actually going to see this giant
window thing here, right, which we showed you before, there's
always this window object in here. And we're going to do
window dot local storage, which relates to if we go to this
application section, this local storage section here, that set
item connected to inject it. So we're going to set a new key
value in here, we're doing it like this because in the future,
maybe you want to do you know connect it to Wallet connect or
c
onnected to Coinbase wallet or something, right, but we're just
gonna say injected, meaning we're connected to that meta
mask. And in some versions of next, Jas next, Jas has a hard
time knowing about this window variable. So we can just do if
type of window does not equal equal, undefined, then we're
going to do this. So we're just making sure that window doesn't
equal undefined that there is a window. So now if I go back to
the front end, and I hit Connect, and we go ahead, and we
connect here
, we'll see now in our application, local storage,
we'll see we've added this connected injected bit here,
we're storing in the browser that we are indeed connected to
this. Now that we've added this into our browser, we can roll
back up to our use effect here and say, Okay, if they're
already connected, great, we'll be done. But before we do
anything, let's check to see if they have this here. And if they
already are connected, let's just run that connect bit. So
we'll say if type of window doe
s not equal, undefined, right,
because we want to check for that window object. Again, we'll
say if window dot local storage dot get item connected,
connected, so if that connected key exists, then we'll just run
Enable Web three. So now We have some functionality in here,
which even when we refresh, it will automatically run enable of
three for us. So now if I go ahead and refresh the front end,
we don't have to press that connect button anymore, because
it goes, Oh, I see that locally, we stor
ed this connected key
whenever you refresh. Now, it checks for this first it sees it
and then runs enabled web three. Now on the other side, though,
if we're in here, and now we disconnect, and then we refresh.
This will show up, which is really annoying. And every time
we refresh, it'll keep showing up, which we don't want. We want
it to be that when we disconnect, we tell it hey,
we've we've gone ahead and disconnected here. So back in
our application, we can add another use effect to check to
see if we've disconnected. So let's create another use effect.
That's going to costly just look for us being connected, we'll
say use effect, do a little arrow bracket thing here. And
we're going to do this anytime. And oh my goodness, anytime I
save, it's going to keep popping up. So I'm just going to just
going to comment that out for now. So in here, we want to say
okay, whatever, there's a rerender we want to run if any
account has changed. And Morales has some functionality for us to
do th
is as well. So from user morass, we're going to import
more Alice, and then down here in our use effect. Inside the
little function, we're gonna say mirallas.on account, changed of account, we're gonna do some
stuff. So on account changed, takes a function as an input
parameter. So we'll say console dot log, account, change to
account. And what we can do is we can check to see if this
account is not. And we can say if account equals equals no,
then if the account is null, we can assume they've d
isconnected.
So we can say window dot local storage, dot remove item,
connected, we'll also run a deactivate web three function.
So from Ross, deactivate, web three will also run, deactivate
web three, which is going to set is web three enabled to false.
So we're going to disconnect the web three, and then we're going
to say console dot log. No count found. So let's try this
out. Now. Let's go back to our front end, we'll do a little
refresh here. So right now it says we're connected. Even
thoug
h in my meta mask, we're not connected, right? And to
start from scratch here, you can go ahead and disconnect it. But
but my browser says, Hey, we're connected, right? So now we'll
connect. And we'll actually be connected right, next connect,
and it and it just overrode connected, you know, with
inject, let's, let's make this injected. And then we'll remove
Yep, okay, injected, we'll refresh, like, connect, and now
it says injected. Okay, cool. So now we're connected here. If in
here, if I just
switch accounts, right, let's go to Account
three, I'll connect. If we go back to our console real quick,
we'll say account change to blah, blah, blah, I can change
back, right, we'll go change account change to blah, blah,
blah. Now, if we go back to our application, go in here. And we
disconnect. Now let's disconnect both of these disconnect. And
disconnect. We'll see it's now been removed from local storage.
And if we go to our console, it'll say, no account, change
the No, no account found
and removed it. Now if I hit
refresh, nothing happens here, I can go ahead and connect. Right
next connect, I can refresh. Oops, I need to go back in here
and re enable this. So sorry, let's add this back in here. But
now it can refresh. Let's go ahead and disconnect here. Let's
connect. Let's go back to the console, we can connect thing
will pop up next connect, I can refresh. It stays connected for
me, I can switch accounts, I can go ahead and here, I can switch
accounts, I can even disconnect
. And it will automatically update
for me, which is what we want. So now we've essentially made a
way more robust connect button, where it goes back and forth
with when we're connected now. Now one more thing that we might
want to do, or application is when we hit Connect, we want to
maybe disable this button, right? We don't want it to allow
it to be able to be pressed. So I'm gonna hit Cancel, we're just
gonna add one more bit of functionality here, we're going
to add this is web three enable
loading. And what this does is
it just checks to see if Metamask has popped up. And so
what our button after the on click section, we can add
disabled. Equals is web three enable loading, so it'll be
disabled if we're loading here. So let's go ahead and we'll
disconnect disconnect. Now we'll hit Connect, and you'll see the
button can be clicked. So that just makes it a little bit nice.
Turn, Next connect Bada bing, bada boom, awesome, we have just
made a way more robust front end than what we ha
d before this
connect button is super slick. And it allows us to kind of flip
back and forth. And our application is incredibly
powerful and knows how to handle all these different changes. Now
that we've learned how to do it the manual way, let me give you
the Chiti way, in our components, we're gonna create a
new file header dot j s, and what we're going to do is we're
going to install this web three UI kit, it's a front end kit,
and it has a whole bunch of these components already built
for u
s. So we can build like a header component and a connect
button component just by using this. So to install, install it,
we're gonna come back here, we're gonna stop our front end.
And we're just going to run yarn, add web three UI kit, like
that. And again, we don't want this to be a dev dependency,
because it is going to be a part of our website, and then we'll
do in our header.js, we'll do import connect button from web
three UI kit. And then we'll do export default function header.
And then
all we'll do is return do like a div. And then inside
this div, we'll do next button. With a little backslash here, we
aren't going to need this for this project. But if we want to
be super explicit, we'll say Morales off equals false. Just
again, to reiterate, hey, we're not trying to connect to a
server here just to make that super explicit. But this connect
button does everything, this manual header thing that we just
created, does. So back in our index.js, we can comment or
delete this line
will do import header from dot dot slash
components slash header. And then instead of manual header,
we'll just do header. We start our app back up with yarn dev
again, we go back to the page, we do a
little refresh here, we now see we have this connect Wallet
button. And it's even looks a lot nicer. It's got some nice
styling to it as well, we can hit Connect wallet. And it'll
give us this little ngModel asking us which wallet we want
to connect to. So asking us which wall we want to connect to
is kind of similar in our manual header to this or set item
connected, injected, right or wallet connect, it would do
connected wallet Connect for trust wallet, it would it would
set item as connected wallet connect, etc. So it allows us to
connect in different ways. And we if we hit Metamask, we go
ahead and connect like so even had some nice styling here where
it gives us our wallet address here. But it also gives us our
wallet balance as well. And again, if we go ahead and
disconnect, we'll
see it automatically disconnect. We
connect like so we can reconnect like so boom, boom. If we switch
accounts, it's smart enough to know that we're switching
accounts. So I know I showed you kind of the hard way. But I
wanted to show you kind of what's going on. It's setting
this local storage in the background so that it knows
which where it's actually connected. But for headers
moving forward, this is all you need. And your life will be
drastically drastically easier. And let's just add a cen
tralized
lottery or decentralized raffle or whatever you want to our
header as well. So it says decentralized lottery, you know
the button Hello. Now that we have that, what else do we need?
Well, well, the main thing that this app needs to do is just
have a big button that says enter the lottery. And then
ideally, you know, show how many people are in the lottery and
then the recent winner as well. So let's go ahead, we'll create
a new component called lottery entrance. And we'll grab that
comp
onent similar to like what we did with our header, we'll
drop this component right here. And then our app will pretty
much be done. So let's create this lottery entrance component,
lottery entrance, that Jas, right and again, in the reason
we're putting these in components, we could 100% Stick
it all, you know all our code in here to make it more modular so
that we in the future, if we want to have more pages or do
other stuff, I'm going to zoom out a little bit just so that we
can see all of ou
r code a little bit easier. This is our whole
index j s. This is our whole header. Let's create a new
lottery entrance app just for the boilerplate code here we're
going to export default lottery entrance, excuse me, export
default function, lottery entrance, and this is just going
to be you know, another component where we're going to
return some that JSX HTML stuff, right? So we're gonna do div and
do like hi from lottery entrance, Excel. And now that we
do that, we can go back to our index.js
. We can do import
lottery entrance from dot dot slash components. Slash lottery
entrance, will stick it right underneath the header like that.
And if we go back to our website, we see high from
lottery entrance so our lottery entrance is going to be right
underneath the header would Here's what we want. And then
we'll delete this line that says, Hello. So lottery
entrance, what is the what is the first thing that we really
need to do in here? Well, we're going to want to have a function
to call
the lottery to enter the lottery. Let's go ahead and do
this. Now let's go back to how we did this with HTML fund me
recall that old fun function like this, but doing it like
this won't rerender. And there's a whole lot of other
functionality that doing it like this won't give us so we're
going to use Morales to actually call some of these functions,
because mirallas has in React mirallas. Again, they have hooks
for us to do pretty much anything we want to do. And one
of these hooks is called u
se web three contract. And what this
does is it gives us a hook that will give us the data returned
from a function called an error returned, a little function that
we can use to call any function. And then we also have these
really helpful is fetching in is loading. So if we want everyone
to have our UI or our website, do something while it's fetching
or while it's loading the transaction, we can use these
two variables to do that. And then all we need to do is we
just need to pass it the contr
act information, which
similar to ethers is going to be the ABI contract address, we'll
pass the function name, and then any parameters for that
function. So we're going to use this syntax here to make that
transaction to the blockchain. And to call these functions. So
what we're going to do is we're going to import us web three
contract from react mirallas. And inside
our function, but before I return, of course, we're going
to say const. And then we'll do kind of exactly what we see in
here. F
or now, let's just get the function, let's just get
this run contract function, because this is going to be the
function that we can call to actually enter the lottery. So
we'll say const, run contract function, and we're actually
going to call this enter raffle. And we'll say equals, use web
three contract, and we need to pass the ABI, we're gonna need
to pass the contract address, we're gonna need to give it the
function name, we'll need to give it the params, which
actually we do know it's go
ing to be blank. But then finally,
we will need the message value. Because if we remember back into
raffle doesn't take any parameters, all it takes is this
message dot value bit. So that's all we're going to need to pass.
So how do we get all this stuff, and I'm going to leave this in
here. But we're going to comment it out for now, because this is
what we need to do. But we need to get all of this stuff into
our code here. So how do we actually get all that stuff?
Well, ABI is easy, right? Abi
isn't going to change at all, no
matter what network or on ABI is always going to stay the same.
Now if you've already deployed your smart contracts, and you
know exactly what address it is, because you've deployed to a
main net, or you've deployed to a test net, all this stuff isn't
really going to change. And we can just hard code it all right
into here. Or we can do what a lot of people do is they'll
create a constants folder. And in here, they'll add like an API
dot JSON, maybe they'll add
a contract addresses dot JSON. And
then they'll add maybe like an index dot j s or something,
we're gonna build our application in a way we can
actually test locally using our own Hard Hat Network, and then
compare it to what it looks like on the actual test net as well.
So we're going to make it network agnostic. So the front
end works exactly the same no matter what network that we're
on. And we can go back, download a directory back into our CD,
hard hat smart contract lottery Free Code Camp,
and spin up our
node here, right with hh node or yarn hard hat node. And we'll
use this as the blockchain that we're going to connect to. The
thing is, if I go back here, and I'm building the front end, and
I go, ah, like this would be better if we did X, Y, or Z. And
maybe I changed the name of some functions, you know, blah, blah,
blah, something else, I want that change to be reflected on
my front end. And I want to be able to code my front end as
such. So since we are the only developer rig
ht now, we kind of
have the ability of where we both know, the back end code and
the front end code. So something that I like to do to make my
life a little bit easier is I like to create an update front
end deploy script. So after we deploy stuff, we run a little
script that will create this constants folder for us with a
whole bunch of stuff, right? It'll give an ABI, it'll give
contract addresses and anything else we might need in our front
end from our back end. So what I like to do is I lik
e to come
back to my original code and an update this for a new script. So
I'll come in here in this deploy script, I'll create a new file,
and I'll call it oh two or even like 9999, up, date, front, and
dot Jas. And the reason I do 99, obviously, is because we want
this to be always the last script in our deploy folder. And
then we can just write a little script that's connected. We just
write a little script that's connected to our front end here
so that whenever we deploy contracts, no matter
what chain,
we can update that constants folder on our front end. So
let's go ahead and create that script right now we'll do module
dot exports, equals async function. And we'll add all our
stuff in here, we don't really need to deploy any contracts,
because we're just updating the front end. So we can just leave
the parameters of this one blank. And the other thing I
like to do, because sometimes I don't care about the front end,
what I'll do is I'll only update the front end if we've specifi
ed
a dot env variable. So I'll create a dot env variable called
up date, front end. And then I'll set this to true. And now
in our script here, we can say if process dot E and V dot
update, front end. And we can just say like console dot log,
updating front end. And now back in here, right, if a mat if I'm
in the correct directory in here, if our own hard hit
Deploy, you know, get this little
updating front end. And now we can update front end. So let's
do it. So I'm actually going to create one
function called
Update contract addresses. And this is going to be our function
that we're going to use to update contract addresses, then
I'm going to make one called Update API, where we just update
the API's and the front end so so we're going to call this
update contract addresses. So I'm going to create a new
function async function, update contract addresses. And first,
we're going to get that raffle contract since we're going to
need to get its address. So we're gonna say const, raffle
e
quals await. And we're gonna do the same thing we've been doing
ethers dot get contract, raffle, and then my VS code auto
imported, it doesn't const ethers equals require Hardhead,
like so. So we have raffle in here. And we're gonna want to
pass this raffle address to our front end, since this is going
to be a variable that we might use a lot of places, we can just
add it like const, front end location, addresses file,
equals, and we're going to give it the relative path to where we
are now, whi
ch is going to be you know, if we see the download
directory to next year's smart contract lottery, FCC, I can
even just copy this, paste that in here. And then it's in the
constants folder, and it's going to be contract addresses dot
JSON. And let's get the ABI file, we'll say const. Front and
a bi file is going to equal and this is going to be nearly the
same thing. So we can just copy paste that. And then we'll do
slash API dot JSON. And now in our update contract addresses
function, we can s
ay const, current addresses, equals and we
can read again, in our front end, we can read from this file.
So I'll usually start out with just two brackets. So that's
like JSON compatible in both of these files. And so to read it,
we're gonna say JSON dot parse, FS dot read file sync, we're
gonna have to import, say const. FS equals require, I know we've
used Fs extra in the past, but this one, we're just gonna use
Fs, which is going to be our front and addresses file, we're
going to read it in wi
th UTF eight encoding. So now this is
going to be our current addresses. And we're going to
update the list of current addresses with some new
addresses. Because our contract addresses we want this to be
chain agnostic, we would do something like for you know, and
then the addresses on rink B, we could do 31337, and then the
addresses on our local host, right, we want to be able to
keep track of all the different addresses across all the
different chains. So back in our function here, then we'll
say if
if network, dot config dot chain id.to string in contract
addresses that were config ideas in there, then we're just gonna
go ahead and add this new contract address in there. But
before we add this new address in there, let's just check to
make sure it's not are already in there. So we're just gonna
say if contract addresses of network dot config dot chain
id.to string, this is so long, I don't want to keep writing that
I'm gonna say const chain ID equals network, config that
chain id.t
o string, and we're just gonna use chain ID. If
chain ID and contract address then if we don't already have
that includes raffle dot address. We're gonna go ahead
and add this new address and then if the chain ID doesn't
even exist, we're gonna go ahead and do contract addresses
current addresses, equals or assuming current addresses of
chain ID equals and then we'll just create a new array, raffled
that out address. So we're saying if the chain ID doesn't
exist in current addresses, we're just
going to add this new
array in there. And then finally, now that we've updated
its object, we're gonna write it back to this file, we'll say Fs
dot write, file sync, front and addresses file. And then we're
gonna do JSON dot string of five. So we're going to
Stringify this JSON object, so we're just gonna go ahead and
write it back. And then bottom module that exports that tags
equals all and then front end. I cool. So we have a function to
update the contract addresses, but we also need the ABI
. So
we're gonna do date API, and we're gonna create another
function async function, update API. And in here, we're gonna do
the exact same thing on raffle equals await ethers that
contract, raffle FX dot right file, sync,
front and abi. It's just going to copy paste it front end, AVI
file. And then to pass just the ABI, we can actually get it
directly from this raffle object, we can actually just do
raffle dot interface that format ethers dot utils, dot format
types, dot JSON. If you look in t
he ethers, Docs, ethers has
this contract dot interface thing which returns an interface
which is different from a solidity interface. But it
allows us basically just to get the API with this one line of
code. So in our back end code here, now if you run h h deploy
or H H node, we should automatically update our
contract addresses in our API dot JSON. So let's go and try
this. So we'll run h h node. So we'll start a node, right in
this terminal over here, we'll flip back to the front end. And
if
we open ABI to JSON, we do indeed, now see, we have the
raffle API in this file. And if we go to contract addresses, we
see on network 31337, here's our first address, right. And if we
deploy to different chains, this will get populated with
different network IDs, and then a list of addresses associated
with them. So it helps make our front ends a lot easier to
maintain, and bounce around and kind of test and work with. Now
that we've done all this, we can actually close our hardhat smart
contr
act lottery Free Code Camp, the hard hat project for this,
and we're just going to have all of our terminals be in here. So
what we're gonna do is we're gonna go down and directory CD
dot dot, and we're going to cd into Oh, sorry, we're actually
we're already in their CDs, hard hat, smart contract, lottery
Free Code Camp. And then in this one, we're going to run hh node
or yarn hardhat node. Now we'll have a local blockchain running
so we can test everything that we're doing in the front end,
ac
tually, I'm gonna move this up one. So now in our first area,
here, we have our front end code running, then we have our
blockchain running. And then in this one, we're just going to
add, you know, whatever we want to add. Now, we can actually go
ahead and hit this little X button here to close the panel.
So that's just hiding the panel, these are all still out, they
only get trashed when you actually hit the little
trashcan. So we're just going to close the panel. But all those
terminals are st
ill running, I promise back. So where were we?
Okay, back to our function here. So we just automated the process
of updating our API's and then updating our contracts as well.
And now we can import these into our files. Now we can import
them one at a time, it'd be like import API, from dot dot slash
constants, slash API, or we could do something a little bit
clever is we could export these in the same file. So if we
create a new file, a new index.js, in here, we can import
them and then export
them in this one file. We can say const,
contract editor says equals require dot slash contract
editor dot JSON, and then const API equals require dot slash API
dot JSON. And then we'll do module dot exports equals API
and contract addresses. So now once we export them like this
back in our lottery entrance, we can import them just in one
line. So we'll say import API comma contract addresses from
dot dot slash constants. So we can just specify the folder
instead of each individual files, becaus
e we have this
index dot j s here, which basically represents this whole
folder. Back in here, what do we have now? Let's uncomment this
abi. Okay, great. We have the ABI we're importing it from our
constants, folder, contract addresses, we have our contract
addresses. And we're going to need to specify the network ID
in just a second here. We have the function name here, which is
going to be what enter the raffle. There are no params. So
all we need to do so how do we get both the chain ID and
then
Also the message dot value will change, it is something that we
can get really easily with Morales, let's comment this
whole section out one more time, just so I can show you something
we can do import. Once again, we're going to get that use
Morales book from react Morales. And what we can do is we can say
const. Chain ID equals use ralis. Now,
the reason morality knows about what chain we're on is because
back in our header component, the header actually passes up
all the information abou
t the Metamask to the Morales
provider. And then the Morales provider passes it down to all
the components inside those Morales provided tags
concentrated equals use Morales. And I'm just gonna do a little
console log chain ID because I want to show you what it looks
like. So if we do a little refresh, and we're in the
console here, we can ignore some of these warnings here, but we
see the chain ID is actually 0x Five, well, because I'm on the
rink B chain or the Grilley chain excuse me, if I sw
itch
back to hard hat localhost, which you should know how to do
from a SEMA fun we bet if you don't have hard hat localhost
and your Metamask go back to that HTML font we bit follow
that along. Okay, great. Now it's going to print this Oh x
bla bla bla bla bla. So that might be a little bit confusing.
But this is the hex version of our chain ID. Right. So let's
switch to Etherium main net. Now we pronounce Oh x one, right?
Oh, x one is the hex version of the number one. So chain ID
gives us the
hex addition of the chain ID. So I don't want the
hex addition, I want the actual number. So what I'm going to do
is I'm going to say Okay, chain ID, your name is actually going
to be chain ID hex. And we can do console dot log chain ID hex,
but I want the number. So what we can do is parse a built in
JavaScript parse int, CIN ID hex like that. Now if I go back,
we'll do a little refresh here. Scroll away from the warning, we
can see the one here. Now let's switch to hard hat, localhost.
We'll
scroll down we see 31337. So use Morales chain ID returns
the hex we parse it with Parson to get the actual number. Okay,
great. So we can stick the chain ID into here. Now this raffle
address is something that we're actually going to use a lot, we
might as well have it be at the top of our code here, we aren't
going to be changing the raffle address. So we don't need to put
it in a hook. We are going to technically be changing the
address when we change networks. But our header app takes care o
f
re rendering and dealing with all that. So we can just make
this a constant variable. So we can say const, raffle address
equals, first let's check the chain ID hex and if there's a
chain ID hex and if there's not a chain ID hex we're gonna do
something else. Excuse me, we're gonna say chain ID hex in
contract addresses. And actually, there's never gonna be
a chain ID hex. So we'll do const chain ID equals Gen ID hex
parsed, Parsons, chinati hex, and you might be thinking, hey,
this, these are
both the same name. Well, what we're doing up
here in use Morales is we're saying hey, pull out the chin ID
object, and then rename it to chain ID hex and down here,
we're saying, Hey, we're going to create a new variable called
Chain ID. So we'll say contract addresses of chain ID at zero.
So in here, we're saying this network ID and this address,
otherwise, we'll just say no. Okay, we're getting there. Let's
uncomment this, now we have the raffle address, and we can just
stick it in here. All
we do now is the message dot value. If we
remember back to our raffle, we actually set that fee
dynamically. So we have in here we do entrance fee equals
entrance fee, which is the parameter in the constructor. So
we want to call this get entrance fee function. This is
one of the ways we can send a transaction. And we can also
send functions, one of the ways that we're going to do it right
when our lottery entrance loads, we're going to run a function to
read that entrance fee value. So how do
we do that? Well, we can
use one of our hooks again, right use effect use effect can
run right when something changes, we're only going to
want to try to get that raffle entrance fee if web three is
enabled. So what we can do is back up in here and use Morales
will pull in that is web three enabled. And we'll have our use
effect in our function, we'll just say if this is web three
enabled, then we'll try to read. So we can go ahead and use this
use web three, contract Wagan. Let's go ahead and j
ust copy
paste this. And we'll use the same setup here. Except instead
of enter raffle, of course, we're going to be doing get
entrance fee. So we're going to get entrance fee. We need the
API we got it raffle address got it. This is going to be the
function name is going to be get entrance fee params nothing
message that value, nothing. We're going to be calling this
get entrance fee function. And now I finally show you how to
actually call one of these in our contracts here. Use web
three cont
ract, download our use effect, we're actually going to
call get entrance fee. Now if we just call get entrance fee like
this, and we say like, you know, const something equals get
entrance fee and then console dot log something, what do you
think's going to happen? And oops, I need to import, I need
to import use effect from react. There we go. We look or logs, I
don't see console dot log, something won't get entrance fee
is going to be an async function. Once again, we need to
wait we would nee
d to do a wait get into T, right? There's an
issue we can't call await in our use effect. So what can we do?
Well, we can actually make an async function, call it update
UI. And then we can stick this inside of the async function
here. And we can call update UI right outside of it like this.
So now we go back to our front end, we do a little refresh. And
if we scroll up, we still see nothing. Well, is web three
enabled actually changes. So the first time that this runs, is
web three enabled prob
ably is false. But when it turns to
true, we want to we want to run this section in our little
dependency array. We're going to add this in here, right? And the
reason that it's false to start with is because of exactly what
we showed in that manual header, right? What does it do? Well,
first, we check to see after we do a refresh, if window dot
local storage get item is connected, then we call Enable
Web three, which will make this enabled. So in our lottery
entrance, is what they're enabled st
arts off as false when
we do a refresh. And then the browser checks the local storage
says, oh, web three should be enabled, let's enable it and
turn to true. So now if we hit save, and we do a little refresh
in our console, we can now see the logged out entrance fee. So
then we'll switch this to entrance fee from contract. Now,
we also probably want to show this entrance fee on our UI, if
we do let entrance fee, we'll say equals blank. And we'll take
this and we'll update, you know, and we'll u
pdate this just
saying entrance fee equals await entrance fee. Cool. Now we have
this as kind of a global variable, we can add it in here.
And then let's even do a weight entrance fee. But this whole
thing in parentheses, and then do.to string, and we can even
console dot log entrance fee. Now we're adding in a new
browser. But there's still an issue here. Let's see if you can
spot it, we'll do a little refresh. We don't see the entrance fee in
the UI here. But we do see it good console dot log
out, right.
And again, this is going to be in a way here, what is going on
here will use effect is going to rerender our browser, right? And
that's what we want is what three enable goes from false to
true, our browser re renders watch, once we get our entrance
fee, does our browser rerender? No, it does not because entrance
fee is just one of these normal variables, right. So we want to
actually change this from being just a normal variable to being
a hook because entrance fee does get updated,
but it's not
triggering a rerender. So we actually want to change this to
being what's called a use state hook. So you can read some more
on the documentation about the using the state hook, it's kind
of the same as doing let entrance fee, you know, equals
blah, blah, blah, and setting it below. But it also will trigger
a rerender for us. And to do it, we actually do const entrance
fee, comma, set entrance fee equals use state zero import use
state from react as well. So basically, entrance fee
is going
to be our value, right, so if we do console dot log entrance fee,
it's going to print out the entrance fee entrance fee is
going to be the variable we call to get the entrance fee, the
entrance fee is going to be the function we call to update or
set that entrance fee. And whatever that this entrance fee
variable is set, we trigger a rerender. From the front end, we
have the state or the actual variable in the function to
update it. And then in the use state here, we just give it its
s
tarting values we're saying entrance fee is going to start
out as zero. So now that we know that, let's go back down here,
and instead of saying entrance fee equals this, we can say
const entrance fee from call equals away entrance fee.to
string and then we can say set entrance fee to this entrance
fee from call. And now when we set the use state, we're going
to trigger a rerender. So entrance fee will actually be
populated. Now if we go to our browser, do a little refresh
here. We can see that
the entrance fee has indeed been re
rendered here and we can actually see it here. We see the
console dot log of zero here even though we're doing console
dot log entrance fee because this set entrance to function
hasn't finished running it basically. So we're just gonna
get rid of that line will refresh and Bada bing bada boom,
this huge number is kind of gross. We might even want to
update it so that it looks a little bit nicer. So once again,
we can import ethers, ethers from ethers. And down
below,
we'll do a little ethers dot utils dot format units. And
we'll do entrance fee from call and we'll do a comma and type
in. And if we refresh on the front end, now we can see,
entrance fee is 0.1. So we can even label this we'll say
entrance fee, blah, blah, blah. Now if we do a little refresh on
the front end, entrance fee 0.1 E. Finally, we have the entrance
fee, and now uses finally on our function to enter the lottery.
So let's finally uncomment this out. And for message value,
we're
going to want to use this entrance fee. And I'm realizing
it's actually better to store this in its raw to string
format. So we're going to undo this part that we just did.
We're going to add it down here. So we're going to ethers, that
format units, entrance fee, comma, ether, like that. So that
like that, and let's comment this back out real quick. So
that at least in the UI, it shows up at 0.1. But on the back
end, we're actually going to save it as its raw, and have way
value. So we're sayin
g that we have this entrance fee and what
we can do uncomment, this, we can just grab this entrance fee,
and plop it in here. So we need to make a button that's gonna do
that. Now again, we want to make our code that all this works,
even if we're connected to a supported chain, though. So
before we even add this, right, if we switch from hard hat to
like, Aetherium main net, do a little refresh. We're getting
kind of get an error here, because we're calling get
entrance fee.to string on an addre
ss that doesn't exist,
right? So it's gonna get really confused and be like, hey, what,
what are you doing here? Let's add a little button here so that
we can actually enter the raffle. Before we actually do
that, let's make sure that we can only call the function so
long as there actually is a raffle address. So we'll do
raffle address. And we'll do this ternary operator once
again. So that only if this raffle address is valid, right?
And it's not, no, we'll add all this code here, right. So we
'll
say if the raffle address exists, we're going to do some
stuff. And for now, we can actually just copy paste this
line, stick it in here. And then otherwise, we'll say div.
Excellence div, close it off. We'll save reformat, great.
We'll say no raffle address, detected. Now we'll just do all
of our code in this section up here. So we have this little div
here, which is great. Inside of this div, let's add a button.
Button button, we'll save it got a little button here, we'll have
it say, ente
r raffle. And we'll say on click, well, we're going
to do some JavaScript. So let's do some JavaScript, we want to
call an async function async function. So I know the double
brackets is going to be confusing, right. But these
brackets allow us to do JavaScript in our HTML, or JSX,
or whatever. And these brackets represent this function, it's
going to an async function. And we're gonna say, await, enter
raffle. And that's it. So let's go back to the front end. Now we
have a little enter raffle f
unction. If we do refresh,
let's just make sure our account is reset here. So we're gonna go
down to settings, advanced, reset accounts, yes. I hit ENTER
raffle. metamath does indeed pop up. And we can go ahead and
confirm. Awesome, okay, we can now enter our raffle. We're
doing incredibly well with this. As you saw, we just got that pop
up. And that was pretty much it, right? It's not very helpful for
the users who are following along with this to look at this
and go, Okay, did it go through? O
r we did it fail, like, what,
what just happened? So what we want to do is create what's
called notifications, right? We want a little pop up saying,
Hey, you sent your transaction, great job, we're going to use a
library. Again, we're going to use the web through UI Kit,
which comes with some notifications that we can go
ahead and use. So if you come to this little interactive thing,
which is right in their GitHub, there's a whole bunch of stuff
in here. And you can actually click around and pl
ay with
different buttons and different things. And you can actually go
ahead and even go to docs for each one of these. And you can
go show code. And you can literally like copy paste code
into your project, like whatever you want to do styling and
everything for us. So back in our app dot j s, we're going to
add import, no to vacations provider from web three UI kit
and inside of the Morales provider, but outside of the
components, we're gonna do notification provider
notification provide Add
it like this. So it goes Morales
provider notification provider component. So wrapping our
component in this notifications thing, and that's notification
provider not notifications. And this is going to allow us to
actually make notifications back in our lottery entrance. Back in
our lottery entrance, we're gonna scroll up to the top,
we're going to import a hook those notifications called us
know, to the occasion from web three UI kit. And this use
notification gives us this thing back called a
dispatch. So I'm
gonna say const, dispatch, equals use notification. So use
notification returns this dispatch, and this dispatch is
like a little pop up that will give us so down here, when we
call this enter raffle, we're gonna give it a little some
parameters in here, we're gonna say on success, these functions
come with onsuccess, they come with onComplete, they come with
an error, all this stuff. And when this function is
successful, we'll do handle success, we'll create a new
handle succe
ss function that will handle the success. And
this is Patrick from the future coming back to show one
additional point. I know I mentioned it, but it is really
good to add this on error, colon error console dot log error for
any run contract functions, even the reads, If any of your run
contract functions break, you won't know. So definitely want
to add this on error error console dot log error to all of
your run contract functions. So up here, before the return,
we'll create a new const handle
success. We'll say this is an
async function async function that takes the transaction as
input parameters. And remember, you can turn functions into
constant variables. And we'll say await TX dot Wait, one, so
we'll wait for that transaction to go through. And then we'll
create another function called handle new notification, dx. And
you'll see why I'm doing it like this in a minute. And we'll say
const and Linoone notification, this is just going to be a
synchronous function because we don't n
eed it to be async. And
we're just going to call this dispatch, we're going to set up
this notification basically. So we'll say notify, or dispatch,
and then we'll add the parameters in here. So it takes
an object as a parameter, we'll say type info. Message, it's
going to be transaction complete, the title is going to
be TX notification. Jose, Shawn, we're going to say, top are and
then icon, we're going to say a little bell. And you can find all this stuff
right in here, type icon position, yo
u can read all about
the different parameters you can kind of set this up with so
handle new notification handle success. So we're saying, enter
raffle once this transaction is successful, call this handle
success function, which is going to call handle new notification.
Okay. And you'll see why we split this into two instead of
just having handle success. Also do the dispatch. When we press
our enter raffle button, we're going to call enter raffle is
successful, we're going to call handle succe
ss. We're going to
wait for that transaction to finish Yes, it passes a
parameter to our handle success function. And then all we're
going to do is we're going to call handle new notification.
And we're going to dispatch we're going to launch one of
these notifications. So back here, let's hit enter raffle.
metamath pops up will confirm. And after it completes, we get
this wonderful transaction notification transaction
complete, right. So we've let the user know, great job, you've
submitted a tr
ansaction. Great work. So this is great. We're
giving our users some helpful pieces here. Now let's add a
little bit more here so that the users know what else is going on
with this lottery. But we need to display a little bit more
data, right, let's display how many people are in this lottery.
How many people are in this game. And we can do that. Of
course, because we have a number players command. We also
probably want to get the recent winner. And we can do that as
well. Up here, we're going
to copy this get entrance fee,
we're going to create another one called Get num players. And
this is going to call go back to raffle dot soul. It's going to
call get number of players actually so let's just call it
call it the same thing. Get number of players and we're
going to call and get number of players. And to store this
value. Once again up at the top. We're going to copy this line,
paste it right and we'll do non players. And then we'll do set
num players. And then we'll copy this line
again. A recent winner
so we'll do recent Winner. Winner will do Set, recent
winner, then we'll copy this again, instead of getting number
players will do get get recent winner. So we'll call it get
recent winner get recent winner in our use effect, let's do more
than just get the entrance fee, let's get everything. So we can
say const num players, from call equals, we'll do a weight, get
number of players.to string, and then we'll do set set num
players do like that. So we'll do set num players
, num players
from call. And then we'll also do const recent winner from call
this is going to be await get recent winner. And we might need
to wrap this to string but I think I don't think we need to.
So we'll do set recent winner paste that in here. So now we've
had a number of players, we've added a recent winner. Let's
come back down here. We have an entrance fee. Let's go ahead and
do number of players. And we'll add number of players or what
did we call it? Oh my god, we called setting emp
loyers twice
oops, it's called non players, scroll down players gonna be non
players, and then we'll do recent winner. And then we'll add in the recent
winner. Awesome. Okay, entrance fees 0.1 eath number of players
to recent winner is nobody here. And if we go ahead and we enter
the raffle metamath pops up. We'll go ahead and confirm. Once
a transaction goes through, we'll get transaction complete.
And if we do a refresh, we see the number of players has
updated. But we had to refresh which is
kind of annoying,
right, let's enter the raffle again, we'll go confirm
transaction complete. But this didn't rerender. Right. So we
want to set something up so that we automatically rerender. And
guess what's going to do that the handle success that we were
talking about before. That's right, all of this update UI
stuff, we can actually pull out of the use effect. So we're
going to copy it all deleted there. And we're going to have
it be its own standalone function like this. And then in
our ha
ndle success, whenever this successful transaction goes
through, we're going to update the UI, right? So handle success
handled new notification, and we're going to update the white.
So now if we go back here, let's enter the lottery. Let's
confirm, we see we get the five and we get transaction
Notification. Now we want to test getting a recent winner
here. So what we can do, actually back in our hard hat
project is we want to create a new script. And I actually
already created it for you, that'
s going to mock the
channeling VRF. And that's going to mock being a keepers. So all
this is really doing is pretty much exactly what our tests were
doing. If you want to pause right now and look through this
yourself, pause here and add this mock off chain, which is
both keepers and VRF. Or you can just go to the GitHub repo here.
Just go to the GitHub repo for Lesson Nine. It's already in
scripts go down to scripts Markov chain, you can just copy
paste it here. Because I want to test that that
recent winner. So
in my hard hat, smart contract lottery, so we'll do yarn,
hardhats run scripts, slash mock off chain dash test network
local host, we're going to mock you know, basically picking a
winner from an upkeep with Request ID one, we're on a local
network. Okay, let's pretend the recent winner was so and so. And
what we can do is we can do a little refresh here, we can see
we have a winner updated. Boom. Now we're gonna clean up the UI.
But I want to talk about a couple of things. Be
fore we do
that, because we're almost done with this section. Something I
want to make really clear because it confused me a little
bit is that this onsuccess isn't checking that the transaction
has a block confirmation, it's just checking to see that the
transaction was successfully sent to Metamask. So onsuccess
checks to see a transaction is successfully sent the Metamask.
And that's why up in that other function, we do TX dot wait one
because that's the piece that actually waits for the
tran
saction to be confirmed. Right now we're using Morales to
make once we call that mocking script, I had to refresh the
browser to see the winner here, right and number of players
obviously got reset to zero, which is great. That's not
ideal. Ideally, we want our UI to just automatically update
when some event gets fired. In our raffle contract, we get this
event emitted. Instead of in our code doing this await success
here. What we could do is we could set up a portion to listen
for that event be
ing emitted and update the front end
accordingly. With that knowledge we can also listen for the
winner event being emitted. We could update our front end
instead of having to refresh it's your cue Yes, and you want
to see if you can add to this right now, I highly recommend
you do. So we've pretty much finished all
the functionality. And wow, you've learned a ton in this
little bit, right? We've learned about use effects use Morales,
all these hooks, all this stuff, and we've got a front end th
at
very nicely handles interacting with our smart contract. The
only thing is, is it looks really ugly. This is kind of
gross. So let's make this look at least a little bit nicer.
There's two things to think about when it comes to building
these front ends. There's component libraries, like web
three UI Kit, which we're using, which gives us kind of like
components that give us, you know, blocks of code, like this
connect button that are already formatted for us. And then
there's CSS libraries t
hat actually will help us format the
rest of our stuff here. So we're using one of these component
libraries. We're also going to use one of these formatting
libraries, and the library that we're going to use is tailwind.
And the reason that we're going to use tailwind CSS is because
it's really popular. If you want to learn CSS, there's some
wonderful resources that you can use to learn CSS web three
schools is one that I've used a ton. So there's going to be a
link to that in the GitHub repo a
ssociated with this course so
that you can make your websites look pretty when formatting
stuff but we are actually going to work with tailwind because
it's going to make us doing CSS stuff a lot easier. Since we're
using tailwind with next Jas. There's actually a wonderful
little guide here for installing tailwind with next Jas. And
we're gonna go basically go ahead and follow along with
this. This link is available in the GitHub repo associated with
this course with this lesson. So we've alrea
dy created our
project we've seeded into our project. Now we're gonna go
ahead and install tailwind npm install dash D instead, since
we're using yarn, we're going to do yarn or pop this open, green
new yarn, add dash dash Dev, paste those three in tailwind
CSS, post CSS and then autoprefixer and it's the three
of these that are going to basically make up tailwind with
next Jas. Once we have those we're going to basically init
tailwind and make a config file for tailwind. So we're gonna do
yarn
tailwind CSS and it does p yarn tailwind CSS init dash P
will run that. This is going to give us this post CSS dot config
dot j s and this tailwind dot config dot j s and what we're
going to want to do is literally just hit this copy button, and
we're going to update our tailwind.config.js tailwind Jas
so that it says okay, all of this stuff, anything in Pages
anything with.js.ts dot JSX or TSX. And anything in these
components, anything in those two folders is going to be
considered tailwind do
uble. We want to use tailwind on these
two folders. Then we're going to add the tailwind directives to
our global CSS files. So if we go back, we're going to styles
Global's we're going to overwrite everything in here
with ADD tailwind base and add tailwind components at tailwind
utilities. And this makes it so that our global CSS file uses
tailwind. Now, you'll see like unknown rule at tailwind, what
we can do is we can go to components, what we can do is we
can look up this post CSS language s
upport extension,
paste that in here, boom, let's go ahead and install this. And
now we get those little underscores to go away, which is
really nice. Now, per usual, we can just do NPM, run Dev and
start adding tailwind to our divs. Now what tailwind does is
allows us in our divs to set everything as a class name, and
then just set some real minimalistic text in these class
names here. So let's look at our smart contract lottery here.
We've just tailwind did it. So it already has been updated a
little bit. Let's update our header here. Well, let's see. We
want to give our header a border from the tailwind. We'll do a
quick search. Look a border we can see all this border stuff
like border width border this border that say we want a border
on the bottom, we can see we can get a border on the bottom with
something like this. Let's do border on the bottom with a
width of two pixels. We just do border b two. So I'm going to
copy border B to do border b two, I'm going to save it and
what w
e need to do for our CSS and everything to take effect.
Go ahead and kill the front end. And then we'll rerun it with
yarn Dev. Go back to our front end now. Give it a little
refresh and Okay, cool. Now we have a little border here. So
we're starting to add some stuff and it's just not a whole lot
yet. Oh, and then we can also add tailwind to me go to
Extensions. We can also look up tailwind. There's a tailwind
extension here. So I'm just going to add a whole bunch of
stuff in here. We're gonna
do flex, flex row. We're gonna make our
decentralized lottery and h one which stands for like header
one. Do class name equals, we'll do the y dash force. We'll give
it padding the top of form the X padding on the x axis of four.
We'll make it bold font and we'll make the text three XL
size. So we'll make everything bigger. Cool. Then we'll do one
more, we'll wrap our Connect button in a div. So we'll say
div class name equals, and we'll give it a an automatic left
margin. We'll do py too. So so
me y padding, some x padding. See
what we have done. Okay. Now if we zoom out a little bit, we can
see, if we close this too, we can see now our Connect button
is on the side here. And they're kind of separated like that. And
I think that looks nice. So we're going to keep that now
we're going to go back to our lottery entrance, we're going to
change this up just a hair, we'll say div class name equals
P. Five, we'll make our button look really nice. We'll say
class name equals background blue 5
00. When we hover over, it
will say, background blue 700. So now if I just say that, and
when we hover over it, it looks a little different, that's
really nice. We'll say text is white, we'll say the font is
bold, we'll give it some white padding, I'll give it some x
padding, we'll have the bunbee rounded, and we'll give it a
margin left auto, now looks a lot better, right? That's a lot
prettier. We're just going to be doing some basic CSS here just
to make it look a little bit nicer, right, but
just that by
itself already made this lottery button look a lot cooler. Now
some functionality that we didn't add here. So we need to
add a disabled, kind of like what we did before. And in our
enter a raffle it comes with, like I said, is loading and it
is fetching. None of our transaction is loading or
fetching, we'll just make this disabled. So we'll say is
loading or is fetching this will be disabled. Right? Now, if we
go back to the front, we hit enter raffle, go ahead and hit
confirm, we
can add a CSS for it. But when a transaction is
loading, they will not be able to click that button anymore,
which that's something else pretty that we want to do
speaking of is loading and is fetching. When it's loading or
fetching, we probably want it to have that like little spinny
thing right when we hit it right now. And mash is pops up. And we
can confirm but it would be cool if it had like a little spinny
thing here, right. So you can usually just Google like how to
add spinny thing or st
uff like that, and you'll get something.
But you can again, you can just copy paste this from my code,
I'm going to show you what I ended up doing for this section.
And we're gonna say is, if is loading or is fetching. And
we're going to use that ternary operator all the time, then in
here, we're going to do a little div. And otherwise, we're going
to do a different div for loading or for fetching, we're
going to add like a little spinny thing in here. So we're
going to add class name equals. If
we're loading, we're going to
add this little spinny thing, which I'll show you what it
looks like in a second. And if we're not loading, we're just
gonna do enter, raffle like that. So we'll come back to the
front end, see answer raffle, we'll click the button. Now we
get this cute little spinny thing, confirm transaction goes
through. So anything goes away. Nice. Well, let's put these on
different lines. So we'll just do so those will be on different
lines. Now boom, entrance fee, number play
ers recent winner,
once a lottery now will confirm transaction complete number of
players has gone up and we have done it. Now this looks a lot
nicer. It's clearly not perfect, but it's much easier to read
than kind of that lump that we had before. And the reason I
wanted to show you this was really just kind of giving you
your footholds for making these look a little bit nicer. This
definitely isn't a CSS course. Oh, wow. Okay, this is
phenomenal. We have an app that we really like. And we're l
ike,
you know what, we want to deploy this bad, Larry, let's talk
about how we can deploy this, this section is going to be
optional, okay, because I'm going to deploy something to
rank B and deploying to test sets can take a long time. So
we're going to deploy our contracts to rink B. And then
we're going to deploy our website to a hosting provider.
So first, let's talk about hosting providers for a quick
second, if we want to host our beautiful website that we just
created, there are ways to d
eploy it using things like for
cell or Google Cloud, or AWS. Nettle. Phi is another really
popular one there, all these different places that we can
deploy our application. Now, the thing about these though, is
that these are all centralized deployment places, having a
centralized deployment application can still be
incredibly important, right? If we look at ether scan for a
second ether scan is a centralized application right at
the end of the day, but it's still one that we've been using
a lot
. However, if we want to have a front end that's
decentralized. Well, that's a little bit harder. The more
important thing for us is that our back end, our smart
contracts are decentralized, right? That's the most important
thing because that will give users the ability to interact
with our logic in a decentralized way. But maybe we
also want our front ends decentralized. Now at some point
we will still use a centralized service Like VSL to deploy an
application, and I'll show you why when we ge
t there, there's
some features that right now, they're really just hard to do
without like a really solid centralized back end. What's
important to keep in mind is that our back end, the logic of
our contract is on a decentralized blockchain. So
even if we host the front end on a centralized hosting provider,
using some type of centralized database to make the front end
easier to work with, the logic of the application is
decentralized. And that's the most important piece. So I'm
going to give y
ou some tools later on, and how to introduce
more of these feature richness. If you choose to do so doing so
will add a centralized component on your front end. And it's
something to keep in mind, depending on how you want your
architecture. So when doing that just be absolutely sure that the
smart contracts on the back end are deployed are decentralized
on one of these blockchains. Now, we'll learn about some of
those centralized ways to do that in a later section. For
now, let's learn how to d
eploy this front end in a more
decentralized way. And the tool that we're going to
use is a tool called IPFS. Now, let me explain a little bit
about how IPFS works. It's this distributed decentralized data
structure. That's not exactly a blockchain. But it's similar to
a blockchain. There's no mining, though. But there is pinning
data, you can add data to this. So let me explain how this
actually works. And you can read how this works on the site,
there's going to be a link to this and the GitHu
b repo
associated with this course. But we give you my basic take on it.
So we have our code, or our file, or whatever it is, right,
we have some piece of data. Now as we know, when you really have
anything, you can hash that thing, you can hash that data,
right, so you can get a unique output. So and that's actually
the first thing that IPFS does. It hashes our data to get a
unique hash, that only points to that data. Yes, a massive code
file a ton of text. Yes, you can encode all of that into
a single
hash function, your IPFS node does this hashing for you. And
every single IPFS node on the planet has the exact same
hashing function, kind of like a blockchain, right, they all kind
of run this same spec, the same specification. So we can hash
our data on our IPFS node and get this unique output. What we
can do then is we can pin that data or pin that code, or pin
that file or pin that whatever, to our node, we have some data,
we get a unique hash of it, all it does is host this data a
nd
have these hashes. That's it, our node is connected to a
network of other IPFS nodes. So there's a massive network of
people running IPFS nodes, they're incredibly lightweight,
way lighter weight than any other blockchain node. And they
all talk to each other. So if I asked the network, hey, I want
to get this hash. All these nodes would talk to each other.
And eventually they'd reach up at our node saying, Oh, I found
a node that has that hash. Here's the file associated with
it. Now, you mi
ght be thinking, Okay, well, that's kind of
centralized, because we have the data on one node here, right?
Well, you're right, well, here's the thing, what other nodes can
do is they can say, oh, that data looks really cool, I want
to have that persist, what they can do is they can pin your
hash, they can pin your data, and they'll get a copy of your
data on their node. And you can keep doing this. And so you
easily allow an entire network to easily replicate any code or
any data in a decentrali
zed sense. And they're incredibly
easy to spin up. And they're incredibly easy to work with
something about IPFS, that makes it drastically different than a
blockchain is the can't do smart contract, there's no execution,
it can really only store it's just decentralized storage that
IPFS can do. Now, the issue here is that in order for our data to
really be decentralized, another node does need to pin our data,
right? Because if we're the only IPFS node that's got this hash,
it's kind of central
ized on our node, if our node goes down,
that data is gone, and the network won't be able to access
that data anymore. So we'll talk about strategies in the future
about having other people pin your data. But for now, this is
a way we can host data, we can send code and have it be in a
decentralized context. So unlike a blockchain, where every single
node in a blockchain is going to have a copy of the entire
blockchain IPFS nodes get to optionally choose which data
they want to pin, and they can
't do any execution. So you could
have an IPFS node, half a megabyte, and you could have an
IPFS node, that several terabytes, it's up to the node
operators, how much data and what data they want to pin. Now
that we know about IPFS. Let's actually deploy our
wonderful application to IPFS so that anybody can use it and
anybody can connect to it. So long as our node is up. Are you
ready? Okay, get excited here. We're first going to do this
kind of the Manuel Wait, because I'm going to show you how
to
install IPFS. And work with IPFS. Hit get started, there's a
number of ways to install and work with IPFS, you can get it
with a desktop application, get a command line. And then we can
also add IPFS to our browser using something like brave or I
think Firefox to some this IPFS router is automatically built
it. But if using something like Chrome, you might have to add a
little companion, because what we want to do is we can actually
use those little hashes as URLs for websites, right, and so
we
want to be able to put that URL in our browser and connect to
that node or that piece of code. So what we're gonna do is we're
gonna have you install the IPFS desktop, so you're gonna hit
that. And when you do that, you should be able to open up IPFS.
Now, if you install it, you might get this little guy, this
little box here in your upper section. Otherwise, you might be
able to open it up with with IPFS desktop and see it as a
regular desktop app. Well, once you install it, you might see
I
PFS is running, you can restart stop, you can do all this stuff,
we're gonna go to the file section, and we're gonna get a
little pop up that looks like this. Now, I've got a ton of
stuff in here, because I've been using IPFS for some time in
here, right now, you might have no data. So let's just go ahead
and import some file. And maybe for now we'll just import, you
know, our next dot config dot j s, right? It doesn't matter,
just import something. And now in here, we have this next dot
config
dot j s or whatever file you import it. So what we can do
with this is we can actually copy the CID. And we can view
this in our browser. So if we do IPFS dot dot slash slash, and we
paste it in, we hit enter, we can give our browser access to
actually rendering IPFS URLs. If using brave, you can just do use
a brave local IPFS node or let's go ahead and download this IPFS
companion. So we'll get IPFS companion, there's a Firefox
install for Chrome brave, blah, blah, blah. So I'm gonna go to
the
Chrome store to get it for brave, we're just going to hit
Add to brave add extension. But once you download it, you'll get
something that looks like this. Even on a little browser
companion, we can see like import, we can see stuff about
our node, if we click our node, we will see a very similar
setup. But now that we have the companion in our browser, we can
copy that Cid that hash. Now brave, we can just do use brave
local IPFS node, and will automatically get dropped into
the file. Now, if IP
FS companion doesn't work for you, and you
can't see the URL inside of something like Google Chrome or
some other browser, what you can do is you can use something
called the IPFS gateway. Now using a gateway, you're not
actually directly requesting the data through IPFS requesting the
data through another server, which is requesting it through
IPFS. But if you are having some trouble accessing these files,
you can use the gateway. So what you'll do is we'll do HTTPS, slash IPFS, slash and then
paste
the hash code there. And you'll be able to see your file. Now if
you do it like this, you won't even need IPFS companion at all.
So we're going to deploy our website to IPFS. So that anybody
else who wants to pin this can, and we will now have the ability
to have an incorruptible, in unputdownable website, which is
just awesome. We're going to learn how to do this the wrong
way first, and then we're going to use a tool that's going to
make it a lot easier for us. Okay, so first, let's go t
o our
website here. And, and if you want to deploy to rink B, go
ahead and feel free. Just remember to make sure that your
contract addresses file updates accordingly. Okay. Now, next,
Jas has the ability to create static websites. And that's
going to be an important term to know we're going to make a
static website. At the moment, we don't want our website to be
tangled with any server stuff. And the reason we we don't want
it to be tangled with any server stuff is because if our website
runs w
ith server stuff, and we deploy it to IPFS, will IPFS
doesn't have the ability to run any code, it just hosts code. If
our front end has any server stuff, it won't work. Now, in
its current state IPFS can't come to our project and know
what to do, right, it doesn't know how to do yarn dev can't do
yarn Dev. So we need to put all of our code into its static
equivalents. So to do that, we're gonna do yarn build. And
if again, if we look at our package dot JSON, it comes with
this build, which just
runs next build. And running this build
command is going to build our code, what's called like a
production build, creating an optimized production build here.
And we'll get something that looks like this. And we can see
this point down here. Static, automatically rendered as static
HTML uses no initial props. There's some server based
applications that next Jas comes with that if we use them. Our
static build won't work. And actually, you'll see when we run
yarn, next export, it'll fail if you
have any that non static
stuff. So let's Go ahead and try yarn next export. And let's see
if it fails, it didn't fail, we now have a new folder called
out. And this is our folder, that's just pure static code and
that we can use on IPFS. In a later section, I'll show you
what it looks like when you don't use some of those static
things. Both Morales and next Jas have the optionality to not
have static code. So we'll just want to keep that in mind. So
now that we have this out folder, we can go
back to IPFS.
And we can import a folder, we're going to import that whole
folder in here. So hit that hit that import button and go to the
folder. Where that is mine is in next year smart contract lottery
out. So now we're going to upload this to our IPFS node.
Once it's done, we'll get this little checkmark. And we can go
through IPFS files and see our output here. What we can do,
let's go ahead and pin this to our node, we'll pin it to our
local node here. And now once it's up, we can copy th
e CID and
go back to brave or chrome or whatever. You type in IPFS colon
slash slash, paste that in there. And we immediately get
dropped into our smart contract lottery in a browser. And we see
hi from lottery, no raffle address detected, because right
now the way I set mine up Was it only works with, you know, our
local hard hats, let's connect our Metamask we'll hit the
connect button, connect, and voila, we are right back where
we were. But with our data stored in IPFS, we can enter
raffle a
s long as our node is running a confirm. And we see
exactly what we get in our local browsers. So this is phenomenal. Now that I've shown you how to
do this, this is the manual way of adding our code to IPFS. Let
me show you the easier way of adding your code to IPFS we're
gonna go to this site called fleek HQ. Go to fleek.co. And to
get to it, I'm going to turn my my IPFS companion note off
because of some of the oddities with working with brave but now
we're here@flickr.co fleek.co makes it ea
sy to to deploy
websites and apps to the new open web permissionless,
trustless, censorship resistant, etc. I like to think about it as
kind of like an auto deployment for our websites. And
additionally, it does some things to help out with that
problem. I was talking about how we want to get other nodes to
pin our data. So it helps us out with that. So let me show you
what it does. So let's go ahead, we'll sign up. And why don't you
know, you can sign in with GitHub. So if you have your
GitHub,
definitely want to sign in with GitHub here. Because
we're going to use GitHub to actually help us automatically
deploy. So we'll authorize fleet to work with our GitHub. You've
authorized your GitHub, let's go ahead and add a new site or add
new site. Now we can use fleet to just automatically deploy
websites, once we push them to our GitHub. So we can come to
our GitHub once again. And click the little plus button, we'll do
a new repository. We'll call this next Jas smart contract,
lottery Fr
ee Code Camp. We'll make it public create the
repository. Let's push all this code to GitHub. We did it once
before, let's do it again. We'll do git add, we'll do a little
dot, then we'll do git commit minus m, say like initial commit
or whatever. We'll do git remote, add origin, and then
we'll grab that URL, paste it right here. And then we'll just
do git push origin, main. Now we go back to our application we
see it in here we can do is back in our fleek. We can connect
with GitHub, we're goin
g to say only select repositories, we're
only going to do this next js application, this next JS get
we're going to install an Authorize. Authorize Great. So
now we're going to pick a repo we've picked a repo, we're going
to choose this application. And we're going to use IPFS as our
hosting service. And now we're going to add our information in
here. So we're going to use the main branch. Here's the repo.
There's gonna be our framework is gonna be next Jas. So we're
do fleek next. Jas, we're us
ing yarn. So we're gonna do yarn
install, and yarn, run build, and then yarn run export. If you
want you could also just do yarn, yarn build and yarn run
export, those are gonna be the same thing. Publish directory is
going to be out and then we just hit Deploy site, yarn, next
export as the last command, not yarn, run, export. The accident
did the wrong one, you go over deploys. Click on this, go to
deploy settings and then Edit Settings and then just change it
to yarn. Next, export save. Then
we'll go back to deploys and
trigger deploy, if you did the wrong one that was just a
learning opportunity for you to learn where the settings are
after you deploy. And what this is going to do is we're going to
do to deploy, it's going to run those three commands yarn build,
yarn export, it's going to run everything. And then it's going
to deploy a site for us both on IPFS. And it's going to give a
regular URL that we can use for normies, if you will. And while
this deploys, you'll actually see
down here we have this thing
called file coin di D in deal proposal, Cid IPFS. Like I said,
we need other people to host our node. File coin is actually a
blockchain that helps you pin your data and uses decentralized
storage to do so. And fleek helps you create those deals and
helps you pin your data with this filecoin Blockchain
filecoin is one definitely to take a look at. And then after a
while, you might have to wait a little bit. And once it's done,
get a little deployed website, we go ba
ck to hosting, we click
on our thing we can see we have like a little website here. And
if we click it, we get a normal URL for connecting and
interacting with our website, you might even see this little
IPFS thing which will connect to your IPFS node. And
additionally, we scroll down in here we can see current IPFS
hash, so we can just stick that in, um, and Bada bing, we have
an IP Fs deployed application. Now what's cool is let's say I
make some changes, you know, I'll go to lottery entrance.
And
I'll do, I'll scroll on the bottom to recent winner, I'll
make a new div. What a close the div off, we'll save git add dot
Git commit minus m, add a dot git push origin mean. In our GitHub, we'll do a little
refresh, add a.as, the most recently added one, go back over
to fleek. Go back to hosting click on the section that we
just made go to deploys, and you'll see there's a new deploy
going through. So it automatically deploys your new
site, it'll automatically create a new IPFS hash for yo
ur new
data. However, it'll still be on this holy bird, you know, or
whatever your URL is here. And this is kind of just a router
for IPSs. So that people without IPFS connected can also connect
to this still. And now that my application is done, pushing
automatically, with fleek, we can see what's up being post in
my application here. Now, like I said, filecoin isn't going to be
a technology, we're gonna go too deep into introduce ourselves.
But like I was just saying IPFS does have this limita
tion. It
doesn't have data persistence, you have to have people pin your
data, in order for it to stay distributed, and stay
decentralized. File coin is a blockchain dedicated to keeping
this data, both decentralized and persistent. And to give us a
better understanding of filecoin. We actually have Ali
here to give us an overview. Take it away, Ali. Hello, I'm Ali, and I'm a developer
advocate here at the filecoin Foundation, which works closely
with protocol labs and IPFS. Just a quick note, p
rotocol Labs
is our r&d arm. So it works on creating tooling and technology
for a truly open and democratic internet and web. And it's
building out some of the foundational tooling like IPFS,
and filecoin, which are two separate projects to enable
that. And hopefully, today, because you're here to build, I
want to impart on you the knowledge and tools you need to
get started with both of those projects. So as anyone that's
kind of played around in this ecosystem, or tech, in general
would know,
data is an absolutely essential part of our
daily lives. And not surprisingly, it's also a super
fast growing field in web three, and one, that's fundamental one,
and it's one of the fundamental necessities of the decentralized
web stack as well. So the current model of centralization
that's grown up, and basically out of a lack of an identity
layer on the internet, is one where only a few big companies
offer storage, and only a few entities hold our data for
authorization purposes. And this is
an obvious problem, both in
terms of being an attack vector for data mining, so without
data, getting leaked through insecure service to third
parties, and also creating a data resilience problem. So
whole services go down every time one of these companies
servers does and we've definitely seen that. So it
really leads to the question, why aren't we designing the web
for the autonomy and resilience we need in the first place? And
how do we store data in a way that aligns with both the
original v
ision of the internet as an open place for knowledge
sharing and collaboration, and, and, and in a way that agrees
with the web three mission as well. So these are the core
problems we're solving with IPFS and file coin. Firstly, IPFS is
a distributed system for storing and accessing files, folders,
websites, applications and data. And it's designed to be able to
work even when the networks between planets, so it's a
distributed by design, it has no central authority servers, and
it's designed t
o be offline first for resilience. And it's
not just a fancy name for another peer to peer network
either. Because the nice thing about the IPFS protocol is the
standard it uses for addressing content on the network. IPFS is
unique because rather than using traditional methods we might be
with familiar with from the web, like those are location parts
that point to a particular HTTP address where your content may
or may not be available and stored. IPFS uses content
addressing so content addressi
ng means that each piece of data,
each meme or even full file system has its own unique
cryptographically verifiable fingerprint, you might call it.
So if you change even one pixel of your main image, for example,
then the content ID or Cid associated with it also changes.
So importantly, this hash function is also upgradeable. So
let's say quantum computing breaks out current secure hash
algorithms, we can upgrade the standard we use. And it means
you will always get the same content returned b
y an IPFS. Cid
as what you expect. So this is fundamentally important, because
when you don't have to care where the data comes from, you
open up the web to massively distributed storage systems.
Hello, decentralization. So now we have a really important and
valuable protocol that enables distribution at scale. And it
provides verifiability of data to serve and retrieve content on
the web. And not just for web three, either, but also for all
web, or tech use cases. The problem is, and it's one t
hat
the early internet also faced, who's going to ensure the
persistence and permanence of all this data on the network. So
unless you're running your own nodes 24/7, or your content is
really popular or other nodes decide to altruistically store
your data. Because I think it's important, then this data can
become unreliable because they're no longer actively
hosted on any nodes on the network's. So to avoid this, you
could also turn to a pinning service that you pay to keep a
copy of your conte
nt around. Unfortunately, the problem with
this though, is that we're heading back towards
centralization of data. And we're creating new data silos
with this solution and losing the trust lessness and
resilience we're looking for. And these one a bad solution
prior to file coin, and it's why they sprung up initially, but we
want a better solution. So this is where file coin comes in. So
far coins architecture, then designed to leverage a crypto
economic incentive model together with cryptograph
ic
proofs in order to ensure data is stored persistently, highly
reliably and verifiably. It uses these cryptographic proofs to
also enable smart contract based permanence. And that means that
it's designed to be as permanent as you, the data owner want it
to be. It's your data, so it's your choice. It's also designed
to enable internet scale capacity. It's currently the
largest distributed storage network in the world with over
18 million terabytes of capacity available, which is apparently
abo
ut 135 copies of the European Union's nuclear program, CERN's
data which, which is kind of a fun fact. It's also file coins
also designed to be and stay hyper competitive on pricing due
to its market economics. And this comes down to storage
deals. So to make this network feasible, filecoin uses storage
deals. And these include two main consensus mechanisms that
ensure both rewards for good actors in the system, and
penalties for bad actors. So when you make a deal with one or
more storage provi
ders to store your important data, the
provider generates a proof of replication. So this proves that
the storage provider is storing a unique copy of your original
data over time to make sure that this data is persisted. These
storage providers must prove that they still have random
subsets of this client data and they create proof of space
times. And these proof this proof of space time is something
that is stored on the blockchain. So anyone at any
time can also check that this is true. And i
t also makes up the
mechanism by which miners are rewarded or penalized because
you have to stake Phil on the network in order to become a
storage provider. So when a storage deal comes to an end, or
user can you can opt to let it expire or renew the deal. If you
opt for renewal, then the providers again bid to host this
content. So this creates an efficient market for pricing a
continual efficient market for pricing as well. It can even go
negative. So the storage provider can even pay you to
s
tore your data if it's an important data set due to some
of the block rewards that are being offered by the filecoin
Foundation as well. So these mechanisms are what built in not
just data permanence, but data timeframes, sovereignty, too so
it's your data, it's your choice. You can decide you want
to store your data for five minutes or 500 years. It's also
your choice over how much resilience you want to have that
data so or how many copies of that data you want to have and
with what store to p
roviders. So this allows you to comply with
regulations like GDPR. And there's a growing number of
tools in the ecosystem like murmurations bit screen that are
allowing for you to do this filtering. But it also gives you
some guarantees that your data, you know, if one storage
provided goes down, you know, surely not 10 of them are going
to go down. So that's a guarantee for your resilience
there as well. And this is why IPFS and
filecoin are great complements of each other. So IPF s gives
you t
hat benefit of content addressing file coin gives you
persistent guarantees that even if your computer or your
favorite IPFS pinning service where to go away, the content
persists. Just as a quick final note on these concepts as well,
IPFS and file coin is separate projects, as I mentioned. So
IPFS is a protocol much like HTTP, whereas file coin is a
blockchain. So IPFS is also storage layer agnostic, you can
combine it with the storage layer of your choice. And while
file coin was specifically
designed to complement it, and
we think is a great choice, you can also store your IPFS data in
the cloud or an other storage solutions as well. So hopefully,
you've got a good baseline for why you'd want to use IPFS, and
filecoin. And for those engineers out there that like a
challenge, and are interested in working on the base protocols
and code of IPFS, and file coin, which isn't always easy for the
average user, I'd encourage you to go and take a look at the
project docs and get hubs and som
e of the associated grants
available for extensions to these open source projects. And
this is a great site here if you want to get more information
into the nitty gritty and really dig into the code behind IPFS
and filecoin. And extend some of that. For those of you that just
want to build out of the box though. And this is definitely a
camp that I often fall into. I want to talk about some of the
dev tooling and storage helpers that make it easy for you to get
started. So firstly, Flake Flake
is one of my favorite IPFS dev
tools. And projects Flake is a CI CD tool that you can use to
deploy your apps for free as simply as easy as you would with
some of the web tools you might web to tools you might be
familiar with like Netlify oversell. The big difference,
though, is fleek uses IPFS to host your site or app. And it
even offers an AES domain routing on their platform. So if
you're deploying a front end app, I would encourage you to
use fleek to make it more distributed instead of som
e of
the traditional web two tools. It's just as easy, I promise. So
another one of my favorite tools is NFT storage, storing your NFT
metadata immutably and persistently, as you already
probably know, is integral to keeping the main value
proposition of NF Ts, then on fungibility. So if you're not
storing this data on chain, which obviously can become
pretty financially unviable for large files, then this is
exactly where NF T storage comes in. So it was specifically
created as a public good to
archive and persist NF T data.
So it's free. And it takes care of the complexity around firstly
creating an IPFS Cid for this metadata, and then making
automatic deals with filecoin storage providers. So it does
this with at least eight storage providers. So eight times
redundancy and it does it with a multigenerational timeframe. So
it automatically renews those deals. Because it's a public
good, it's all free as well. And it's also super easy to use,
because you just need because it's a JavaS
cript service. So
you just need to say import that as an NPM package or JavaScript
library and then call the API and NFT dot storage takes care
of the rest. For data that isn't NF T metadata as well, we built
web three dot storage, web three storage is designed to give you
those same web two benefits. So similar to NF T dot storage,
make it super easy for you to use. And it's got JavaScript and
go client libraries. While giving you you know the
power of IPFS and filecoin of decentralized storage
and IPFS
content addressing so it's got one terabyte after terabyte, one
terabyte of free storage with that. So try that out if you're
not just like trying to store and obtain metadata. The next
tool is a bit more advanced. It's called textil Palghat gate,
and it's for you know, more advanced developers or those
looking for more flexibility to interact with IPFS live peer to
peer and filecoin. It's a Docker container wrapped around
filecoin and IPFS nodes. And it gives you a lot of options to
c
onfigure it's a minor selections and extend
functionality. It also offers some bridges to several layer
ones which might be of interest to developers out there. Another
one here and I'll preface this by saying you need an invite to
this is ESRI tech. So it's for people looking to store really
meaningful public data. It's currently in alpha mode and like
I said, it requires an invite because it's been built as a
public goods specifically to store important information. If
you are I do have Use ca
se around this slide, please feel
free to reach out to us on this project. Other final tool I'll
mention is orbit dB. So many people coming to the web three
space from web two are often looking for the same sort of
relational databases that we're so used to in traditional
computing except in a decentralized or distributed
format. And this isn't an easy problem. So Oba to me is
currently in active development. And because this isn't an easy
problem to solve, this isn't an ideal solution for those
of you
looking for an out of the box experience. But if you are
looking for something like that, try out orbit dB. And there's
also several other tools in the ecosystem leveraging IPFS and
filecoin, including ceramic, which is similar to textil power
gate, except it uses decentralized identities.
Lighthouse is file drive, and there's even Morales has an IPFS
API, so check those out as well. So storage is really a
fundamental component on one technology system. So and
there's so many use cases y
ou could dive into here. And so
hopefully, I've provided you with some of the knowledge and
tools you need to get started with IPFS and file Goyt. Coin
and really make powerful distributed applications. And
there's just one more tool that's also in active
development now. So if you look closely at this diagram, you'll
notice probably unfamiliar logo right at the end of the logic
layer. And that's the logo for the filecoin virtual machine. So
FBM will be launching at the end of this year. And we'
re super
excited about it. And it's going to allow smart contracts,
contract use combined with like colocation of storage data,
loads, so computing capabilities with storage capabilities, and
will also be EVM compatible. So as I said, we're super excited
for the kinds of use cases that we're going to see come out of
this project as well. And you can follow along here on the
website here as well. So hopefully, I've given you all
the tools you need to get started with IPFS. And file
coin. If you d
o need more resources or want to get
involved, we have proto School, which is interactive tutorials
on decentralized web protocols. There's also NFT, school dot
Dev, or join a hackathon, check out our hackathons.filecoin.io
page for all the latest hackathons we're involved in.
And if you do really want to dig deep and build tooling in IPFS,
and file coin or build a cool project, check out our grants
options as well for that, in the meantime, all please learn long
build and prosper. So we've lear
ned a ton in this
section. And that is it. So let's do a summary of all the
amazing things that we've learned. And then we'll go into
the TypeScript edition of this because the TypeScript edition
is definitely a little bit different. So let's talk.
Alright, so first, we learned more about next Jas. And we
learned we can have an application using next Jas. And
it's a framework that's gonna allow us to build really
powerful front ends and full stack applications really
easily. We learned about the
layout of our next JS project,
we add components in a components folder, which are
basically minimalistic blocks of JavaScript and HTML that we can
use to modularize and create our website out of these components.
Constants is a folder that we can put constant variables, Node
modules is node modules. And out folders. What happens when we
export all of our code to a static example, pages are going
to be basically the routes or the different pages of our
website, everything goes through app.js. P
ublic is just some
public stuff styles is for any CSS or styling of our
application. And then we have our basic files here. In our
pages section, we have our app, which is surrounded by both this
notification provider and our Morales provider, all of our
components run through this app on all of our pages run through
this app. So this is kind of considered the entry point for
our entire application. Having this Morales provider wrapped
around our notifications in component means that we don't
ha
ve to pass parameters between our components. And our lottery
will just know what chain ID that we're on, because our
header is going to pass it up to Morales provider and the Morales
provider is going to pass it back down to our lottery
entrance. And we saw with our manual header, the way that that
connect button works behind the scenes. So it's doing some local
storage, where we're storing whether or not we're actually
connected, we learned about use effect, and you state and these
different h
ooks in our front ends were one of the main
reasons we want hooks is we want we want our websites to
rerender. When stuff changes, we want our components to be able
to talk about the state of the blockchain with each other. And
they're incredibly powerful for building our React applications.
Use effect is one of the most popular ones where if we don't
have a dependency array, our function inside of our use
effect will run anytime something re renders a blank
dependency array means it'll just run
once on load. And if
there are dependencies in the array, it'll run any time any of
the variables in those change. We also learned about the use
state hook, which is really similar to saying like, let
variable equals x. But it also comes with the RE rendering
ability. And it comes with some other nice abilities that we
didn't really discuss here, we learned how to call different
contract functions with mirallas. Not only sending
transactions, but also calling data. Morales is smart enough to
kn
ow that when it sees get entrance fee that this is going
to be a view function. And this is going to be a transaction, it
can tell the difference between the two. So this one's going to
populate Metamask to pop up. And this one just going to return
kind of normally like a view function, what we can actually
use the same syntax between sending transactions and then
calling view functions on our contract, we added a button,
calling one of these Morales pieces and then had an onsuccess
section wher
e when our transaction completed, we update
the UI and we add a little pop up for notifications, we learned
how to deploy our code directly to IPFS. And use that IPFS hash
to interact and see our code, we also learned about fleek and how
fleek automatically deploys to IPFS. Whenever we do a git push
to our GitHub repository, and it makes continuously updating our
websites much easier. It also gives us a regular canonical URL
as well. And then finally, we learned about IPFS and
decentralized data
base storage. Now you might be asking, Okay,
well, why don't we just store all the data for this website on
Aetherium, or polygon or avalanche, etc? And the answer
to that is that can get incredibly expensive storing
data, storing a ton of data on the blockchain costs a ton of
gas, whereas this is a much cheaper alternative. Aetherium
avalanche and the smart contract platforms aren't really meant to
be data storage layers, they're meant to be logic layers, right?
Decentralized logic, decentraliz
ed smart contracts,
oftentimes, yes, we're gonna have to store data in them. But
when it's a ton of data, there are better solutions. And there
are different solutions out there for storing our data, like
IPFS and filecoin, you should be incredibly proud of yourself, if
you've made it this far, because you've just made a really solid
app, a really solid front end application. And you've learned
how to really easily add functionality for interacting
with your smart contracts. So give yourself a p
at on the back,
maybe even tweet this out, share this really cool application
with your friends and family. Take a break. And I'll see you
in the next lesson. All right, welcome to one of the
fastest lessons that we're going to have here. And in this
lesson, we're going to talk about the hardhat starter kit.
Really quickly, I'm going to walk you through it and show you
how to use it. Now we've learned a lot about projects, we've
learned a lot about different repos, learnt the basics of
smart con
tracts. And we've learned a lot about front end as
well and building front ends for applications. So this smart
contract kit repo comes packed with a ton of starter kits that
you can use to start deploying your projects right away. And as
you can see, that hard hat starter kit is easily one of the
most popular ones with the most stars the most forks out there.
The smart contract kit repo actually comes with a ton of
frameworks, like if you want to work with SWANA, if you want to
work with Python
and Brownie, if you want to work with foundry,
truffle, really any other framework out there, you can get
started, clone one of these repos, work with one of these
repos and build your project and get started right away, we're
going to show you how to use the hard hat starter kit. So you can
just grab the repo and go and already have some boilerplate
code and a boilerplate really good looking repo to start your
projects with, we come to the smart contract kit, hard hat
starter kit repo here. An
d if you're working with GitHub, you
can just go ahead and use this template and it will
automatically generate you a new GitHub repo with the hardest
starter kit. So let's go ahead, we'll click use this template,
we come up with our own name here, we'll call it make it
public create repository from Template. it'll generate our
repository. And now we automatically have it in our own
repo here. And we can get started working with it. And we
can get started working with it. If you don't want to cl
ick the
use that template button, we can also just copy the URL. And in
our code editor, we can just do git clone and paste that in
there. So for now, I am going to get clone, but I'm going to get
clone with this repo that we just created. Come back in with
the git clone, hard hat play FCC or hard hat starter kit. We're
going to cd into our head play FCC, and then open that up in a
new code editor. And awesome. Now you'll see in this repo, it
comes packed with a ton of contracts, deployments, sc
ripts,
tasks, test, everything, you name it to really get started in
a professional environment. We look in the contract section, we
can see we have a couple of sample contracts. We have a
contract for making an API call to a chain link node, working
with keepers working with price feeds, and then working with
chain link Vir Fe two. We've got some test contracts and we
additionally have this fuzzing folder, which we'll talk talk
about in a much later section of this course, we have deployed
scri
pts where we start with deploying mocks, then we deploy
each one of those contracts, we have a sample script to read the
price from one of these contracts. And we have a whole
bunch of sample tasks. Now at the time of recording, instead
of scripts, this repo uses tasks. But again, they're a
little bit interchangeable. And of course, we have some unit
tests and some staging tests as well, that you can go through
and take a look at, once we're in this repo, we can run some
familiar commands here,
we'll do yarn, of course, to install our
all of our packages. And then everything that we're going to
do, if you get lost, you can always come back to this repo.
And you can follow along with getting started and the
Quickstart. So we just did the git clone. Now we're doing the
yarn, and then we're gonna go ahead and run yarn hard hat
test. This hard hat starter kit repo is very consistently up to
date, we did the last push being just a few days ago, and will
constantly have some best practices f
or building our smart
contracts and having a really professional coding environment.
And it's got this really cute logo. Once we've installed all
the dependencies, we can run yarn, art hath test, we can run
all of the tests in the test folder, which also will show us
how to interact and how to use all these different contracts in
here. And they each have some console dot log, so you can see
more about what's actually going on when these tests actually
run. If we look in the Hardhead config.js, i
t's got some really
familiar code in here, we have all our imports at the top, we
grab a whole bunch of environment variables, we've got
the ether scan plugin, we've got the gas reporter, the contract
sizer, which is a plugin that tells you how big your contracts
are. So named accounts, different solidity versions, and
then mocha timeout as well. We can of course, do yarn hard hat
node, which will run through our deploy scripts, and then spin up
a new note for us, which has mock chainlink tokens
, mock
Oracle's mock aggregators, and mock up VRF for us to go ahead
and interact with once that's up, we can then of course, to
hard hat, console, dash dash network localhost, and begin
interacting with contracts on localhost. So we can kind of
follow along with price feed, for example, and do const price
consumer B three equals await ethers dot get contract, price,
consumer V three. And then we can do await price
consumer B three dot get latest price. Let's wrap that in a to
string. And we can
see a mock latest price from a contract
that uses channeling price feeds. And we can interact with
any of our contracts and work with any of the mocks as well in
here. If we want to deploy this to an actual test net like
Rinckey, or main net will just pop in our dot env file, we'll
close our node terminal, and we can run yarn hardhat or just hh
deploy. And then we'll add whatever tags we want to do
here. So let's just deploy our price feed contract. If we go to
the price feed deploy, we scroll
down, we'll get the tags. Okay,
great, we'll use the feed tag, dash tags feeds dash to or feed
dash dash network brings beam. And while we're waiting for this
to deploy, we can go back to the actual repo. And just make sure
to follow along with the documentation here and the
Quickstart in all the usage and everything so that you make sure
that you're working with the most up to date version. There's
even documentation on running a local network using a test net
or live network working with Aethe
rium. Rigby adding your
private keys and dotting envies all this stuff that you already
know, forking, which we'll learn a little bit later, auto funding
your contracts for working with chain link API running tests,
you can additionally run your tests in parallel by adding the
dash dash parallel flag to our tests. We can interact with our
deployed contracts with those different tasks that we've
created linting code formatting, estimated gas code coverage
fuzzing, we'll talk about later. And then
contributions. PRs
issues are always welcome here. And once it's outputted, and
even verified, if you have verification turned on, you'll
get a little task that we can run to just go ahead and read
the price feed or interact with the contract. So we can copy
that task out. Yarn hardhat. Read price feed since it's a
task here, contract the contract data which we just deployed
network Rigby. And we'll get reading data price feed from
consumer contract on network, Rigby price is here, which of
cou
rse, we're saying the price of Aetherium is $3,033, because
it has eight decimal places. So if you're ever looking to start
a new project, and you want some boilerplate code, this hard hat
Starter Kit is a great place to get started. And of course, you
can open it and get pod if you want to just test it out and try
it and get pot in a Cloud Shell. So that's it for this lesson.
Wasn't that fast. This was the fastest lesson ever. So if you
want to do a little extra learnings here, I would fork
thi
s I would clone this I would use this template, try to play
around with the repo a little bit yourself and see what you
recognize, see what you don't recognize and keep that prepped
in your mind for later so Questions in the course. And
then for everyone here who is TypeScript, there is, of course,
a TypeScript version of this as well that you can get cloned.
And it has a nice blue logo here to show that it's a little bit
different. So, that being said, use the repo, have fun. Let's
get to lesso
n 12. Alright, now we're moving on to
the hard hat ERC. 20s are the section where we're going to
learn how to create our own ERC 20 or EIP 20 Or B E P 20 or AEP
20 any of these tokens on the blockchain before we can
understand what an ERC 20 is, or even what one of these tokens
are, we first need to understand what is an ERC. And then also
what is an EIP in Aetherium, and avalanche and finance and
polygon. All these blockchains have what's called improvement
proposals. And for Aetherium, they're
called Aetherium
improvement proposals, or E IPs. And what people would do is they
come up with these ideas to improve Aetherium or improve
these layer ones like polygon, Matic avalanche, etc. And on
some GitHub or some open source repository, they'll add these
new EIP is they'll add these new improvement ideas to make these
protocols better. Now, these improvements can really be
anything, they can be anything from a core blockchain update to
some standard, that is going to be a best practice f
or the
entire community to adopt. Once an EIP gets enough insight, they
also create an E RC, which stands for Aetherium request for
comments. So EIP a theory and improvement proposals ERC a
theory and request for comments. And again, these can be like BP,
Pep, you know, etc. For all these different blockchains.
Both the improvement proposals and the Request for Comments,
all have these different tags, now they're numbered
chronologically, so something like an ERC 20 is going to be
the 20th ERC s
lash EIP, the ERC is and the EFPs share that same
number. And there are websites like EIP is that aetherium.org
That keep track of all of these new Aetherium improvement
proposals, and you can actually see them real time go through
the process of being adopted by the community. Now, one of these
e IPs or ERC is, is going to be the ERC 20 or the token standard
for smart contracts. This is an improvement proposal that talks
about how to actually create tokens and create these smart
contract tokens
. I made a video about this recently. So in the
GitHub repo associated with this course, we're going to have a
sub lesson, and we're going to watch a quick video that
explains more about these different tokens. Now first,
let's define even what are ERC 20s So ERC 20s are tokens that
are deployed on a chain using what's called the ERC 20 token
standard, you can read more about it in the year 20 token
standard here link in the description as well. But
basically, it's a smart contract that actually
represents a
token. So it's token or the smart contract. It's both it's
really cool tether chainlink unique token and dye are all
examples of ERC 20s Technically, chain link is in the ERC. 677 as
there are upgrades to the ERC 20 that some tokens take that are
still backwards compatible with ERC. 20s And so basically, you
can think of them as ERC 20s with a little additional
functionality. Now why would I even care to want to make an ERC
20 Well, you can do a lot of really cool stuff with it. Yo
u
can make governance token, you can secure an underlying
network, you can create some type of synthetic acid, or
really anything else. In any case, how do we build one of
these ERC? 20s How do we build one of these tokens? Well, all
we have to do is build a smart contract that follows the token
standard, all we have to do is build a smart contract that has
these functions that has a name function, symbol function,
decimals function, etc. All these functions we need to be
able to transfer it, we
need to be able to get the balance of it
etc. And again, if you want to check out some of the
improvements that are still ERC 20 compatible, like the ERC 677
or the ERC 777. Definitely go check those out and build one of
those instead. All right, awesome. Now that we know what
one of these ERC 20s is, we can go ahead and create our own per
usual in the GitHub repo associated with this course. We
have all the code available here. If you want to just get
clone. This is going to be again another o
ne of our quicker
lessons here. So we're in our terminal. We're in our VS code
here. We're going to make a new directory. I'm going to call it
hard hat ERC 20 FCC will cd into hard hat, your C 20 sec. And
we're going to create a new hard hat project the exact same way
we've been doing it yarn add dash dash dev or tab. Let's
actually open it in its own VS code will do code.or file open
this folder. And okay, we're in our project now. Let's create a
new hard hat project. We'll do yarn, art hats. W
e'll do create an empty hard
hat dot config dot j s here and great. We've now got an empty
hard hat dot config dot j s. If you want to copy paste your hard
hat dot config from a previous projects you want to copy paste
your heart Have dot config or your dot EMV file, because you
know we're going to need those feel free to do so now, I'm just
going to update this to 8.7. I'll add the rest of my stuff
later. So as we've heard this EIP 20, or this ERC 20. All it
needs is to have these functions in
its token standard so that we
can transfer tokens, we can do all the stuff in the ERC 20
contract itself, it really is just keeping track of how much
of each token people have. So the smart contract, kind of in a
weird way it keeps track of itself. To get started, we're
going to do this kind of the manual way. First, we're going
to create our own manual token here, or a really minimalistic
one anyways, so let's create a new folder. contracts will
create a new file called manual token dot soul. Y
es, I'm going
to show you kind of the hard way to make it and then I'll show
you a much easier way to make it. So to get started here are
usual we can do pragma, solidity to carrot zero, point 8.7. And
then we'll even do spdx, license identifier, MIT do contract,
manual token, and boom, let's get started. The main reason
this token smart contract works is that there's some balances
mapping. So we have a mapping of addresses to you in 256. And
it's usually public called balance of and all this do
es is
this mapping is obviously the key is going to be every single
address on the planet, and then how much they have. And
basically, when we transfer tokens, transfer tokens, we're
basically just subtract from address, amount, and add to to
address. So a really minimalistic way to implement
this would be to create this transfer function first. So
we'll create this function, I'm going to call it underscore
transfer, we can do an address from address to you into fifth
sixth mount. And now we'd p
robably put some requirements,
we probably omit some events, and let's make this public as
well. And really, at the end of the day, is we're gonna say
balance of from minus equals value, which is the same as
saying bounce of from equals bounce from minus value, or
excuse me, amount, and then we're gonna say balance of two,
excuse me, plus equals, which is the same as saying, you know,
balance of two plus. And technically, that's really all
we need, right, we probably want to do some asserts ins
here,
some requires to make sure all of our numbers make sense. But
really, at the end of the day, this is all that this function
is doing. Transfer works when the caller is sending money
directly into another address. But what happens if we want to
allow some smart contract to work with our token, or we want
to allow somebody else to work with our token, you know, maybe
to deposit it into a protocol, or do some more functionality
with it, there will be some approved function that will
approve t
hat contract to do that. And then we'll have a
function transfer from and this function will, you know, it'll
just implement taking funds from user and this will be public as
well. And then at the top will be some type of allowances
mapping that will tell who's allowed which address to take
how much token, which sounds a little confusing, but let me
just add the mapping. So it'll be a mapping of addresses to a
mapping of addresses to an amount to a unit tivity sex and
this will be public allowan
ce, we're gonna say address, Patrick
is going to allow address of Patrick's brother to use 25
tokens. And that's how this allowance works. And in our
transfer from transfer from will check this allowance mapping and
save Hmm. Did Patrick give you authorization to borrow those
tokens? Oh, yes, you did. Okay, we'll let you transfer from. And
I'm just, I'm just going to copy paste an implementation of it,
you can check out the GitHub repo as well. And it would look
something like this is we check t
he allowed amounts, update the
allowance, and then transfer the tokens. So those are some of the
main functions. So we need an approved function, obviously, to
update the allowances here. And usually, you'll have like a un
256 initial supply. And this will be like how many tokens
there are starting with how many tokens there are total,
sometimes you'll add a mint function to add more functions.
But you can basically start to see this contract ramping up.
One thing we could do is we could go ahea
d go through this
spec and just line by line, you know, build our token ourself.
And after we do that, it might look something like this. So I'm
just copy pasted the code from the GitHub repo. If you go to
contracts, manual token, I just copy paste this code in here.
This is what a contract a token contract might look like. Okay.
So we have all these functions, we have all these arrays. We
have all this stuff. And you can see in the constructor, we're
taking initial supply, and then a token name
and a token symbol.
The name, you know, might be something like dy token. And
then the symbol might be something like dy, just so that
it's easily recognizable just by its name and its token. coding it all from scratch, like
that is definitely something that we can do. But as
engineers, we know that that's probably really annoying. And we
don't actually want to do that. So what can we do instead? Well,
we can use an open source library, like open Zeppelin, to
actually get some boilerplate code
work with open Zeppelin is
almost considered kind of the standard library of solidity.
They have a list of open source contracts that anybody can use
and import into their contracts that have a ton of boilerplate,
so that you don't have to manually write everything out,
we can see all their code in their GitHub repository, open
Zeppelin slash open Zeppelin contracts, and we're going to be
using them a lot moving forward. So for example, you can see kind
of on the left side of their documentation
, they have this
tokens section, and they have an ERC 20, which is one of those
tokens standards. If you scroll on here, they even have some
minimalistic examples of how to create your own ERC 20 token.
And that's what we're going to be using to build our token.
Because you see how much smaller this is how much less code this
is to maintain. Let's go ahead and let's use open Zeppelin for
us to create our token. So let's create a new file. We'll call
our token dot soul. I'm going to create our ow
n token here. So
let's do spdx. License identifier, MIT, we'll do
pragma, solidity carrot zero, point 8.7, we'll do contract our
token. Now, what we're going to do, we're going to import
openzeppelin contracts into our hard hat project. And we're
going to do it the same way we did with chain link and any
other packages in the future. So we'll do yarn, add dash dash
Dev, at open Zeppelin slash contracts. And this is going to
add the open Zeppelin slash contracts NPM package to our
project. And on
e of the code pieces that they have is this
ERC 20 contract that we can use and we can have our token
inherit all the functions. So we'll go ahead and import it
with import at openzeppelin. Slash contracts slash tokens
slash ERC 20 slash ERC 20 ditzel. And just by importing it
like this, all we have to do is have our token inherited now. So
we'll say contract. Our token is ERC 20. Boom. And just like that
our token is almost done. Now you might get this little wiggle
this little red line here sa
ying our token should be marked
abstract. And that's because if we look into the ERC 20 dot soul
of openzeppelin We'll see that it has a constructor. So in
order for us to inherit your C 20 token, we have to use the ERC
20 constructor and we just need to give our token a name and a
symbol. But we can say in our constructor, we can leave it
blank. And then right next to our constructor, we'll add the
ERC 20 constructor and our name will be our token. And then our
symbol will just be OT and then t
his ERC. 20 token also comes
with something called a mint functionality, which is
essentially a function that allows us to create tokens,
right because right now we actually get initialized with
zero tokens, right. So nobody's actually allowed to have any
tokens. So we want to mint the initial amount of tokens and
then who owns all those tokens to start with. So usually what
you'll see is you'll see a mint function like this, it'll be
passed in message that sender, so whoever deploys this contra
ct
will own all the tokens to start. And then we'll give it
like an in their soul supply. And then we could do like, you
went to 56 initial supply equals like seven, or whatever. But
instead, a common practice is just to add it to the
constructor. So you and 256 initial supply like that, as we
know about solidity decimals don't work so great. So if I say
my initial supply is 50, that 50 is going to be like 50 way, and
there's all these ERC 20s come with a decimals and decimals
function, which te
lls us how many decimals we should expect
with our ERC 20. The default is 18. And we can override this
function if we want a different amount of decimals. And if we
know the default is 18. And we want to deploy 50, we might want
to do our initial supply of 50 e 18. Or you can also say like 50
times 10, raise the 18th or whatever you want there. And in
our code when we deploy this now, this is actually where
we're going to finish the project. Because everything else
that we would do here, we've a
lready done, all we need to do
is make a deploy script and write some tests. That's really
it. Because right now you have all the skills that you need to
write a deploy script and then optionally write some tests for
this project. So I highly encourage you to pause the video
here and try to write your own deploy script. And even if you
want to write your own tests, you can always refer back To the
GitHub repo associated with this lesson, as we do have a deploy
script in here, we also have a Type
Script edition in here as
well that we're additionally not going to go over. And of course,
if you get totally lost, there's a ton of instructions in here to
help you learn more and help you work with this specific
repository. So let's do a quick review of
what we just learned. So ERC 20 tokens, or EIP 20 tokens or or b
p or p p, or any of these dash 20. Improvement proposals are
what's known as the token standard. And the token
standard, these tokens on chain actually just tokens that are
smart
contracts. Now, these tokens are obviously different
than the layer one tokens like Aetherium, or polygon or
Avalanche or arbitration, those are not going to be smart
contracts, those are going to be blockchain native tokens, and
you'll hear me refer to it as blockchain native tokens a lot.
versus these tokens, these ERC 20, these smart contract tokens,
which are just smart contracts. And they're just kind of a
combination of these functions that represent how many tokens
each address has, we c
an create our own token with all the
specifications added. Or we can just use openzeppelin to import
a token in now another popular repo like open Zeppelin is going
to be this one from Rory capital called soulmate. And they're
both aimed to be standard libraries for solidity. And one
of the important things to keep in mind is that these tokens
have this allowance mapping. And you can allow other addresses to
have access to your tokens and move your tokens around. This is
important, especially wh
en we get to later on when working
with defy when we want to give some smart contract access to
our tokens so that they can input it into their defy
protocol. It's also a little bit tricky. And you want to make
sure you're not allowing malicious contracts to interact
with your tokens. And we'll also see that when we start to
interact with these tokens more before any contract can interact
with our tokens, we need to approve them to interact with
our tokens. And that's it. Now you're a token wiza
rd, and you
can deploy your own tokens. Take a break, get that coffee, and
I'll see you in the next one. All right, welcome to the next
session, we are going to be learning about defy in this
session and going to be programmatic, and going to be
programmatically interacting with the defy protocol. I am
incredibly excited for you for this session, because defi is
one of the best use cases for smart contracts. And one of the
use cases that I am specifically most excited for now, as I've
mentioned,
the past defy stands for decentralized finance, we've
left some links in the GitHub repository for you to learn more
about Defy. One of the main reasons we're so excited about
defy is because we move away from this area of traditional
agreement. And that's what smart contracts are all about. They're
about removing this centralized entity, from our financial
world, and especially from these financial institutions that have
a conflict of interest. They're in business to make money not to
keep our
money safe not to make us money. And we want to work
with a system where everything is transparent, especially when
it comes to our financial services. So we want to move to
this world of smart contracts, especially when it comes to our
money. And in my mind, defi is going to be the industry that
affects the masses, the quickest because of how much fair how
much better decentralized finance is than centralized
finance. And at the moment, the rates and the yields and the
interest that you gain i
n defy is much better than centralized
finance, because remember, will go away from these centralized
protocols, saying, hey, trust us, we'll give you access to the
markets or hate trust us put your money in us will keep your
money safe to this cryptographic math base guarantees instead of
having to trust these companies and these entities, which is
what we want. And additionally, the more our Oracle networks get
better, and the more Oracle's networks work with these smart
contract platforms lik
e Aetherium, like polygon, like
arbitrage them, the more data and the more complex financial
products that we can do. Now, one of the other reasons I'm so
crazy excited about defy is if you look at this little chart
right here, it shows the different markets by size. Now
this this image is a little bit outdated, but it still shows you
the relative sizes of all these different industries defy right
now is a $200 billion market, there's about $200 billion
locked in the defy industry. And I'll show
you that in a minute.
Cryptocurrency actually, at the time of recording isn't 360
billion. It's actually like 1.8 trillion. So it's a lot more
than this. But still, it's a massive subset of all these
other areas like gold is a $10 trillion market. The stock
market is almost $100 trillion global real estate, almost $300
trillion derivatives as quadrillion dollars. So the FBI
is a super tiny, tiny, tiny, tiny subset right now and in my
mind, all of these areas can be re landscaped with Defy. So
w
e're ramping up, we're getting there. So it's gonna be up to us
to make some of these protocols and make it Much easier for
people to get into the space where their finances are going
to be more fair, more accountable and more
transparent. And with better yield rates, we can actually see
a pretty good summary of what's going on in defy this site
called defy llama. It shows total value locked in all these
different decentralized protocols. And we can see a lot
of these are across multiple chains,
right. And a lot of
these are EVM compatible chains, Aetherium, Biden, smart chain,
avalanche, Fanta, drawn polygon, all these are EVM, compatible
blockchains, where we can see exactly how much money
independent users have put into these protocols. The time of
recording Ave is the number one protocol for total value locked.
So there's $22 billion locked in Ave, which is the protocol that
we're going to be going over today. So what is Ave So are they is a
borrowing and lending protocol, it allow
s us to borrow and lend
cryptocurrencies. So we can actually put down a token as
collateral, it's kind of similar to like putting money in a bank,
and earn yields on other people borrowing that collateral from
us almost exactly what a bank does, except for the fact it's
what's called noncustodial. To the Ave team never touches our
money. Nobody ever touches the money. It's all just a smart
contract. It's all just this programmatic code. So we can be
rest assured, no one's going to run off with o
ur money, no one's
going to do anything bad. And we also gain these higher yields.
borrowing and lending is a critical piece for creating
really any interesting financial applications. If you want to
short sell something, if you want to leverage up on some
asset if you want to. If you want to do more complex
financial products, you need borrowing and lending. Now a lot
of the typical FinTech or financial technology or finance
terms do apply here. And this course isn't going to be a deep
dive int
o exactly how these financial products work. And
it's also not going to be a course on finance. If you want
to learn more about finance, we'll leave a number of links in
our GitHub repo associated with this course. So you can learn
more about finance and become what I like to call a defy
quant, quantitative defy engineer. And I am so excited
for more defi quants to get into this space. Alright, so here we
are in the Ave application. It's at testament dot Ave dot
markets, we are in the testament
of Avi now, everything that
we're going to do here is going to work on Main net as well, but
we're going to use it on their test net. Now this is actually
obvious older UI, and they have a new website that looks even
better than this. But we're going to be going through a lot
of the basic functionality depositing taking out a loan,
potentially even shorting an asset, if we want, I don't
recommend going to the site because it might not work and
COVID the way you'd expect it to. So for this, just
sit back,
relax and watch. In order for us to actually short sell or margin
trade. The first thing that we're going to need to do is
actually deposit some collateral, we need to deposit
some collateral in order to borrow this way. If we never
repay back the loan that we took out or the the amount that we
borrowed, Ave, we'll just go ahead and take the collateral
that we put in here, we'll do what's called a liquidation
call. And that's why this is actually a little bit safer than
short selling i
n traditional markets. Because if your
collateral is less than how much you have borrowed, you'll just
immediately get liquidated, but you still lose a bunch of money
so like don't get liquidated. So what we want to do now is we're
gonna scroll to Aetherium, we're going to connect our wallet
here, we're going to move to COVID test network browser here.
And we're gonna go to this deposit piece. So it already
shows our balance here we have point two COVID eath. Let's
deposit 0.1. We're gonna hit d
eposit Metamask
can pop up, confirm. But once this goes through, this means
that we've have it deposited and we go to our dashboard, we can
see we have some eath here, point one eath got some API,
this is kind of like that percentage return that we're
gonna get back for, for depositing into Ave. And yes, we
can use it as collateral here, we have this, this marked as yes
here. So that's exactly what we're going to do. And it says
nothing borrowed. Yep, we can go ahead and hit this borrow now
butt
on. And we're gonna get brought to the borrower screen,
and we're going to choose which asset we want to borrow. Now,
whenever we borrow one of these, there are these API's, right?
This is the percentage that over the course of a year that we're
gonna have to pay, in order to actually borrow this asset. The
stable one means it'll always be for or variable means it
actually changes depending on kind of how much liquidity the
protocol has, you can kind of pick which one you want to do
stable is yo
u're always gonna be a 4% variable is going to be a
little bit riskier, but you might get a lower fee. So we're
actually going to borrow some dye right, because dye is a
stable coin. It's worth $1. In a way, you could call this taking
out on margin because we're taking out dye to borrow and
another way we could say we're shorting dye which is kind of
funny to think about, but you get to choose how much you want
to borrow here and you'll see this this thing called Health
factor I'm going to zoom
in a little bit this thing called
Health factor here as we as we scroll this thing. So this
health factor is how close to being liquidated you are
remember how I said you can get liquidated this health factor
represents how close we are to getting liquidated. This means
means how close we are to always saying, You know what fu we're
taking your funds. If it goes below one at any time, somebody
can liquidate you and take a lot of that deposit that we put in,
there's some math behind actually what
the health factor
actually is, you can head over to the Ave documentation, which
I will leave a link in the description to kind of read more
about the health factor. So we're going to borrow 29 die
we're buying, we're borrowing basically 30 bucks. Gonna hit
continue, I'm gonna do a variable, zoom back out,
continue, we're gonna borrow metamath pops up, confirm
transaction that's pending. And we're gonna go to the dashboard.
And now we can see kind of our new balance here, right, we can
see the
point one eath, deposited and 29 die. And we can
see our health factor up here, you can even click this little
button, saying, hey, it represents how close you are to
being liquidated, we can see the value here, our eath is worth
like $200, or die is worth 30 bucks. So we're good. We're
pretty healthy here. In order for Avi to understand and under
to price the underlying collateral, so it knows how much
it can lend out. Obviously another one of these protocols
that uses changing price feeds to p
rice, the underlying
collateral, many of these billion dollar defy protocols
use chain link on the back end to do all their pricing
mechanisms. And that's essentially it, we could then
repay our debts, we could borrow more assets, we could swap
assets around. And the interest return, we get onto pausing our
assets is amazing. So now that we learned a little bit about
how to use their UI, which is hosted on IPFS, by the way,
let's go ahead and let's learn how to do all this and do even
more progr
ammatically so we can become defy quanta engineers. Now, like I said, we're going to
be working with the Ave v two protocol, if you want to try out
the v3, you absolutely can, if you can go there and play with
it right now, it still has more money locked in it, which is
great. But the v3 protocol is obviously the latest addition.
So we're gonna be flipping back and forth between the
documentation and our code base. So I recommend that you have the
documentation up as well. And per usual, all of
the code that
we're going to be working with is in this hard hat defi Free
Code Camp repository. So let's jump in. I'm in by VS code, I'm
in my folder with this course, we're going to make a new
folder, we're going to call it hard hat, defy FCC, we're going
to cd into it. And then we're going to open it up with code
period. Or you can also do per usual, File Open and then open
that folder. Now we're in a new project, we're going to do yarn,
add dash dash Dev, aren't hat. And we're going to add h
ard hat
and start up our minimalistic hard hat project. Once again,
for starting up your minimalistic hard hat projects,
I usually just copy paste from another folder, or I just used
that hard hat starter kit that we saw in the smart contract
code repo. But whatever works best for you to get your project
started, you can use now that we've got this, we can run yarn
hardhat. And we'll just create an empty hardhat.config.js. Now
to save us some boilerplate time, I am going to copy paste
my hard ha
t.config.js from a past project into this one, just
to make it so we don't have to go through that boilerplate
setup again. And I'm also going to copy paste this line from our
heart at smart contract lottery. Again, if you want to use your
package JSON or your yarn dot lock to install dependencies,
you absolutely can. But I'm just going to paste that in here and
run it. And then I'm going to copy paste over my prettier
files, so that all my JavaScript can be formatted the way I want
it to be. Ok
ay, great. Now we have a minimalistic project spun
up, let's go ahead and get started learning how to interact
with the AVI protocol here. So let's make a quick readme and
talk about what we want to be able to do. So first, we're
going to want to be able to learn how to programmatically
deposit collateral. And if we stopped right there, that might
be enough, we'd be able to programmatically deposit
collateral, and in doing so we earn yield, we'd earn that
percentage return just on our deposited
collateral. So
accomplishing this by itself is already a feat. But let's say we
want to go one step further, we want to get into these more
interesting financial products. So after we deposit some
collateral, we're going to learn how to programmatically borrow
and other asset, the deposited collateral is going to be eath,
slash wrapped eath, which we'll talk about in a little bit,
we're going to borrow another asset, which for this demo is
going to be dye. And the reason that we're using dye is
because
dye is what's known as a stable coin. So dye is actually a token
on the blockchain created by this maker Dow where the price
of the dye token is always pegged to $1. So we're putting
down eath as collateral and we're borrowing cryptocurrency,
US dollars, sort of, we're borrowing this token, which
represents a US dollar and then we'll just repay that I will
repay almost everything back and you'll see why we don't repay
everything back in a minute. One other protocol that I want to
talk ab
out quickly is the uniswap protocol. And the
uniswap protocol has become this haven for trading. It's a
decentralized application that allows us to trade assets and
tokens with each other on something called an automated
market maker. It ends basically a decentralized Stock Exchange,
but with tokens and again, tokens aren't exactly stocks,
they're very different. So when looking to get some of these
assets It's like wrapped Aetherium or dye or link token.
On a main net, oftentimes, you're going
to use one of these
decentralized exchanges. And obviously, these decentralized
exchanges are much fairer, because everything that happens
on them is transparent, much fairer than centralized finance,
everything that happens on them is transparent. You can see
everything that's going on on chain, which is absolutely
phenomenal. So let's go ahead and get started here. And let's
just create our scripts folder. Because in this project, we're
actually not going to create any contracts ourselves, we'
re just
going to learn how to interact with these protocols. If you do
want to learn how to build some of these protocols, at the end
of this session, we will give you a ton of links. And we'll
have a special guest explain a few different ways to learn how
to build more of these decentralized protocols.
Although we've already built one with our decentralized lottery,
which is fantastic. Let's create some scripts here. And for all
of these things, deposit collateral, borrow and repay.
We'll just
put this all in a new script called Ave borrow dot
Jas. So we're gonna do everything in here. And since
this is a script, it's going to have the same setup, as we've
seen before, right, so we're gonna have this main thing
around an async function. Main, and then we'll have our imports,
of course at the top. Now something that's important to
note, go to avi, we go to the protocol, we kind of read
through the docs, and eventually we would find out the protocol
treats everything as an ERC 20 token.
And but we know that
Aetherium or the native blockchain token that you're
using, isn't an ERC 20 token. And the reason that treats
everything like an ERC 20 token is that it's just much easier,
right? If everything's using this ERC 20 token standard, it's
much easier to send in and interact with stuff. On a lot of these protocols,
when we go to deposit Aetherium, or polygon, or arbitrary marks
cetera, what actually happens is they send your Aetherium through
like what's called a web gateway, an
d swaps it for weath,
which stands for rapt ether. And it's basically Aetherium. But in
an ERC 20 token contract, so what we want to do is going to
do that same thing, we're gonna skip kind of using this web
gateway. And we'll just get the Web Token ourselves, and we'll
use that as collateral. So in our script tag, I'm going to
make another file called Get weath.js. And in here, we're
actually going to go ahead, and we're going to build a script
that will deposit our token for web token. Okay, s
o let's create
the script. And there's a link to the web token on ether scan,
and on Main net in the GitHub repo. And the way it works, is
you actually deposit Aetherium. And in return, it'll give you
the web token on Rigby right now, by deposit 0.05. Go ahead
and write I'm connected to ether scan, I'll go ahead and write
this transaction, I'm gonna drink pee. So I don't really
care if it's if it's the actual contract or not, because it's
not real money. But I'm going to deposit 0.05 eath. And a
fter our
transaction goes through, we copy the contract address, we
add this token to our Metamask the same way we added link will
import tokens, paste the address in here, add custom token,
import tokens will now see we have some wet token in here, I
deposited 0.1 before so 0.1 plus 0.05, which is why it shows 0.15
Because I did it twice, well, while I wasn't recording. So um,
but this is how you can get this wet token into your contract.
And then anytime you want, you can call this withdrawal
function. And you can withdraw your Aetherium out of this and
do what's called Burn your wet token. So when you swap back
from weath to Aetherium, you hit this withdraw and boom, you
basically swap them back because this contract itself right now
is holding your Aetherium token. Pretty cool, right? So in our
get wet function, we're not going to add this main thing
here, we're going to create get West here just as kind of a
module and we're going to import it into our Ave borrow. So we're
not goi
ng to do this this main thing that you see here.
Instead, we're going to create an async function called Get
Wes. And then below, we're going to export it. So we'll do module
dot exports equals get weath. And we're going to export it so
our Avi borrow can use this get West script that we're creating
right now, let's add let's go ahead and do this. Well, in
order to interact with a contract, we're going to need an
account. So we can do const deployer equals await get named
accounts, per usual. We
'll do const get name accounts equals
require hardhat. My VS code automatically imported that
thank you VS code now. And now we want to call the deposit
function on the west contract. How do we call this deposit
function on the west contract? Well, how do you call any
contract? Well, what do you need? You need the ABI and then
you need the contract address. Drill this in. You're always
going to be the API and the contract address to interact
with the contract. We know that if we give we know tha
t if we
give our project the whole contract, it'll get the API but
we also know If we just do the interface, that's just as good,
it won't give us all the functionality, but it will tell
it will give us the API, it will tell our ethers, what functions
it can use. So we're going to create a new folder, create our
contracts folder. And in here, we're going to create a new
folder inside of that called interfaces. And this is where
we're going to create our web interface. Now a web interface
is goin
g to be really similar to an ERC 20 interface. So if you
want to go ahead and try to add it yourself, feel free to do so.
Or what you can just do is you can come to the GitHub repo
associated with this lesson, come to iOS and just copy paste,
right, you'll see the functions in here are exactly the same as
an ERC 20, allowance approved balance of decimals named blah,
blah, with these additional deposit and withdrawal
functions. So I'm going to copy all this, move back over to my
hard hat project,
we'll create a new file, we'll call I left dot
soul. And just to note, oftentimes, a good best practice
is to have interfaces, start with eyes so that you know what
to interface and then we'll paste it in here. Now in order
for this to compile, we'll need to use 0.4 point 19 version of
solidity. So what we can do is we can come over to our, our
head.config.js. We have solidity compilers, version 0.07, or
whatever versions that you have in here, we can add or just
replace. So I'm going to put a
comma here, version 0.4 point
19. Save. And now in order to make sure we can get that API,
do yarn, hardhats compile, or hh compiled, and great, we compile
this interface. So now we have the ABI to interact with. Now
that we have the ABI, a little check mark here, let's go ahead
and get the contract address. But for reasons that we're going
to learn about very soon, we're actually going to work just with
main net set of getting the Rinkeby test and address, I'm
going to look up with main net, we
're going to find the web
token on Main net. So I'm going to copy the address of main net.
And again, you can just grab this address from the GitHub
repo associated with this lesson as well. And for now, we're
gonna say check a little check mark here, and paste the address
there. So now we're have the ABI compiled from an interface, and
we have the contract address for main net. But let's go ahead and
create this contract now. So we can say const, iOS equals await
ethers dot and then we'll need
to import ethers from Hardhead
as well. Ethers dot get contract at this is another one of these
functions on ethers, it allows us to get a contract at a
specific address, we'll say get contract app, we use the iOS ABI
for now, we'll just hard code this address in here. And then
we'll connect it to the Deployer. So we're saying let's
get this web contract with the ABI of iOS at this address
connected to Deployer. So we could go ahead and run await AI
with dot deposit. And we'll set value which wi
ll be some
amounts. Let's go ahead and at the top, we'll say const amount
equals let's do ethers dot utils dot parse, ether, and then we'll
do 0.0 0.02. So we'll deposit 0.02 will say const TX equals
that will do await TX dot wait one wait for one block to go
through. And then we'll just get the balance will say const. With
balance equals await I with that balance of employer. So we're
going to call the balance of function on our iWeb ERC 20
token. And then we'll just do console dot log got West
balance.to string. So we're using the main net
address in here. And we're gonna say okay, we're going to deposit
some amount, we're going to wait. And then we're gonna go
ahead and get the balance right, so we're just depositing our
Aetherium so that we can get that ERC 20 version of
Aetherium, that West token here. Now, you might be thinking,
Okay, why are you putting the main address in here? Let's
Patrick, slow down. Let's go ahead and let's create a mock
Web Token contract address. Let's de
ploy the mocks first,
and then we'll go ahead and use that same setup that we've been
doing this whole time, why you why you directly hard coding
this in here. Well, I've been alluding to this for some time.
But there's another way that we can run tests in our smart
contracts. And this is with something called main net
forking, we can actually do something where we fork the main
net, and run a local hard hat node. That's pretending to be a
main net node. And all we have to do is update our hard
head
config to do so. So let's talk about forking for a minute. So
on the left here, we have a blockchain an example of
blockchain it's going to be something similar to a test net
or main net like Rinkeby, eath, main net polygon etc. This is
going to be a blockchain that we deploy to. Now there are a whole
bunch of blocks in here, right? We have this huge chain that we
can work with. And all this information on the blockchain is
public information, like this block is going to have
transaction tr
ansaction transaction, each one of these
blocks is going to have a whole bunch of transactions. And all
this information is on this public blockchain. In addition
to all these transactions, it's going to have things like price
feed contracts, it's going to have things like Ave contract,
the Web token, contract, etc. All this contract information is
public. So hypothetically, if it's already there, we should be
basically able to copy this to our local environment and do
some simulations ourselves
. And that's exactly what forking
does. A fork blockchain literally takes a copy of an
existing blockchain like on the left here and brings it on our
local computer. We actually have control over our blockchain
that's running locally, because it's gonna run on a local
computer, similar to hard hat. Now, everything we do on this
local forked blockchain is not going to affect main them,
because it's a simulated blockchain. It's just running in
our local environment. So we can actually interact wit
h this
forking this kind of local blockchain that resembles that
mimics the actual blockchain. And here's what forking doesn't
do, it doesn't download the entire blockchain into our local
setup. Anytime we reference an address. Anytime we reference,
hey, there's something at a specific address, we make an API
call to our Aetherium node, which again, we're using alchemy
and say, hey, oh, what's at this address, and it'll return just
that specific contract for us. This way, we don't have to
downlo
ad the whole blockchain. And it's a lot quicker. And we
can also do this forking to run our tests to run our scripts to
do everything. And now you might be thinking, wow, Patrick, this
sounds awesome. Well, why don't we just do this for everything?
Well, there's some trade offs. The pros are that it's quick,
it's easy, and a test will resemble what's on Main net. Now
the cons are that we need an API, and we can't do everything
locally. Some contracts, some contracts are complex to work
with, and
mocks might just be better. But using a forked
network might be a good way to run your tests, it might be a
good alternate to just using mocks. So it really depends on
what's right for you and right for your project. But it is a
fantastic tool, especially for something like Avi, where we
want to quickly test some things. Now the hard forking
also will give us a bunch of fake accounts. So we'll still
get a bunch of fake accounts on Main net that will be given a
theorem. So we'll get fake main ne
t accounts for this forking.
So for the rest of this, we're going to be using this forking
to run our scripts and run our tests. If you want to go back
after this and try this all out on COVID. We've got a whole
bunch of different addresses for the COVID network so that you
can run these scripts directly on COVID. And you can see the
transactions yourself. Just note that when using COVID, you'll
want to make sure that you're using the same addresses, as are
in the AVI docs for the COVID network,
because they do change
sometimes. Great, let's go ahead, we'll go to our RT head
dot config dot j s. And now we'll go to our networks, which
right now, I don't have anything, I only have ranking.
And we'll add hard hat in here, we'll add a little comma down
here. And we'll say, you know, the chain ID of course is going
to be 31337. We'll add this forking keyword. And in here,
we'll say the URL for a Forking is going to be our main net RPC
URL. And this is another reason why we're using alchemy.
Alchemy
is fantastic at these forked blockchains, and has really good
pieces here. So what we can do is we can come back to our
alchemy dashboard, we'll create a new app. This one will be for
Aetherium main net, and we'll say forking chain. We'll say for
forking, we'll go ahead and create this on a theory and main
net. Now that we have this forking chain, we can do the
same thing. We'll grab our API key, we'll come back to our
project, create this new file, we'll create our dot env. And
we'll d
o main net RPC URL equals and paste that in there. In our
Hardhead config, we are now going to be forking from mainnet
RPC URL whenever we work with the heart at blockchain. So now
that we have this in here, let's go ahead and try to run this get
wet function. Because since we're forking the blockchain, we
should be able to go ahead and simulate this. So backing off a
borrow, we'll go ahead we'll do const, get weath equals require and then we'll
pull this script that dot dot scripts slash get we
ath. And
then, in our main function, we'll just run await yet. So to
run our script here, we'll do yarn hard hat, run scripts
avevamo.js. And our default network is hard hat. So we could
either do dash dash network hard hat or just run it. And
remember, since in our config, we're saying, hey, when we run
the hard hat chain, use this forking, we're going to be
forking, so let's go ahead Run this ran to air main. Net RPC
URL is undefined. Well, that makes sense. Let's go ahead and
add this consumm
ated RPC rel equals process study and v dot
made it RPC URL. Let's try this again. And we now see we got,
you know, this much weath, which, again, that much wealth
is gonna be 1-234-567-8910 1234567 0.02
wealth, which is exactly what we want. So now we have a way to
interact with main net locally, which sounds kind of crazy. But
he's incredibly powerful for exactly what we're doing right
here. So we have a way to fork main that eath and run our
scripts and kind of test to see and simulate what i
t would be
like to actually run these transactions. So we're gonna do
like that, our get with function looks good. And obviously, we
would still modularize this, we put this in our helper Hardhead
config, but we're going to skip doing that for now. So let's go
head back to the AVI borrow. And let's go ahead and flesh the
rest of this out. Now, let's go ahead and set up the rest of the
boilerplate here. So we'll do const Deployer, because we need
an account, obviously equals await, get named acco
unts. And
then we'll do const get named accounts equals require. Now we
want to start interacting with the ABI protocol, well, what do
we need, we're gonna need the ABI and the address, nothing
changes, we're gonna need these two. So what we can do is we can
go to the Ave docs. And again, we're going to be on V two. And
we can go ahead and find the contract address in here. Now
the way it works is they actually have a contract, which
will point us to the correct contract. The contract that
we're
going to be doing all the lending with is this lending
pool. And there's actually a contract to get that contract
address. And to get the lending pool address, we have to go to
the lending pool address provider. It's this contract
that will tell us what the address of the lending pullets.
And we can actually see the deployed contract section, we
can see the address of this contract. And we can see the
address of all the main ones and the COVID. Once again, if you
want to play with this on COVID
. But we can see lending pool
address provider is going to be located right here, we can copy
this address. And we'll just take notes of this. And that lending pool, we're
going to get from the lending pelagics provider. So let's
actually create a function that will get us the lending pool
address from the lending pool address provider. So down here,
we'll create async function, get lending pool. Now in order to
interact with this lending pool address provider. Same thing,
we're going to need it
s address and its API, we have its
address. So let's go ahead and get its API, you can go ahead to
the GitHub repo associated with this lesson. And just copy paste
it from the interfaces section. You could also look directly on
the blockchain to see what this contract looks I can create your
own interface. Or we can go ahead and use the interface
right in the documentation. So we have islanding Palach.
Provider, I'm just going to copy this from the ABI docs. But
again, you've got a number of opt
ions. And so in our contracts
in our interfaces, let's let's stick iwth in interfaces here.
Let's create a new file. And we'll call it I lending pool
address provider, dot soul. And we'll paste it in here, we just
see we're using 0.6 point 12. So be sure to in our Hardhead
config to make sure we have that we don't so we're just create a
new one 0.6 point 12 And then we'll compile yarn Hardhead
compile and compiles so awesome. We now have the ABI here, we'll
get this contract by saying const lend
ing pool address
provider equals await ethers dot get contract at will say I
lending pool address oops address as I lending pool
addresses excuse me, I'm going to update the name I Lenny pull
addresses provider islanding pool addresses provider, we're
going to pass in that contract address that we got from the
Ovid docs here. Paste that in. And then we're going to connect
it to our Deployer. So we're going to have our get lending
pool, get past that account variable and then we'll just use
the a
ccount here to connect it and the account that we're going
to pass is of course going to be our Deployer. In our lending
pool address provider. There's a function called Get lending pool
which returns the address of the lining pool and this is going to
be the function that we're going to call so we're gonna say const
lending pool address equals a weight lending pool address says
probes address any pool address says provider dot get lending
pool and that's it we'll have the lending pool address a
nd
then we'll have this contract give us the lending pool
contract by doing const lending pool equals await ethers dot get
contract Add. And we need to do the same thing, the interface,
the interface, the address, and then the account. So back in the
docs, we can grab the I lending pool by copying this like this.
We'll go back to our contracts interfaces, new file, I lending
pool, that's all. And we'll paste it in. Now with this one,
if we scroll to the top, we noticed that we're importing
from
some local places that we actually don't have in our
contracts area, we can once again go ahead and add the AVI
protocol v2 from NPM. And just use this as our imports. So
we'll do yarn, add dash dash dev apt have a slash proto call
hyphen V two. And now that we have the Add Ave protocol in our
node modules, we can update these imports to point to our
node modules instead of our local files. So I'm just gonna
go ahead and tell you that the islanding pledge provider is
that Ave slash protocol hyph
en V to slash contracts slash
interfaces. And then data types dot soul is gonna be at of a
protocol hyphen v2 slash contracts slash, proto call
slash libraries, slash types, data types, that's all again to
make sure this is right, yarn, Hardhead compile or hh, compile.
And Cool. Looks like I did that right? Lending pool equals await
ethers dot get contract at, we're going to be using lending
pool, I lend the pool here, we're going to use this lending
pool address got from the addresses provider.
And then the
account, which is going to be our Deployer. And now we can do
return lending pool. And if we want, and now that we have this
function get lending pool backup in our main function, we can say
const lending pool equals a weight, get lending pool and
then pass the Deployer. And then we can even do a little console
dot log, lending pool address and then do
lending pool dot address. And since we're making it forking,
we can kind of just keep running this right. So we'll do yarn
hardhat
run scripts. Ave Baroda Jas, there are multiple
artifacts for contract II lending pool addresses provider.
And this is because in our node modules, import all this stuff
from contracts and in here, and there's already in islanding,
pool addresses provider in those add ons slash contracts. So
actually, we don't even need this, I lending pool added
service provider, we can go ahead and delete it. Our time
right now is getting confused. It's saying Oh, are you
referring to the one that you download
ed from NPM or the one
that you made, which which one do you want to use. So we'll
just make it easier for hardhat. And we'll say okay, well, we'll
delete the one that we created, we'll use the one that we've
downloaded here. And now we should be good to run this
again. Because now there's only one for to pick from, which is
the one we downloaded from NPM. And perfect, we get our get
weath printout here, and then we get lending pool address is
here. And this is going to be the actual lending poo
l address
on ether Aetherium main net. So if we go back to ether scan,
copy that address and paste it and ether scan, we can see it's
even labeled Ave v two, and we can see a ton of transactions
going through all the time. And ether scan is having a hard time
keeping up with all the transactions. So we've got the
lending pool address, we've got some web token, what do we need
to do? Now we want a deposit of what do we need in order to
deposit the token? Well, if we look at the deposit function i
n
the avec GitHub, we can scroll in here and we see it eventually
we'll call this safe transfer from which is basically going to
be this transfer from function, since we're calling transfer
from it's going to be this contract, that's actually going
to pull the money out of our wallet. So in order to give the
Ave contract, the ability to pull the money out of our
wallet, we need to do what we're going to need to approve the
contract. So first, before we can even deposit we're gonna
need to approv
e it to get our web token, we're gonna have to
get the Web Token first. So let's get the Web Token address
we'll say const Web Token address, equals and this is
where we marginalize it and get it from our hard head helper
config. But for now, we can just hard code it, the Web Token
contract address is going to be the exact same thing as what's
in get web and then want to approve. So let's write an
approved function because we're going to use this a couple
times. So we'll make an async function a
nd approve ERC 20 And we'll take a contract
address a spender address, which is going to be the contract that
we're going to give the approval to to spend our token and amount
to spend so exactly how much we want to prove it. And then an
account to do all this on. So in here we'll say const ERC 20
token equals await ethers dot get contract at. And we could
say I WEF but maybe we just want like a simple ERC 20 token
interface. And we're going to grab that by cheating a little
bit coin to our hard
head defy FCC. We're going to go to
interfaces and grab this interface from here. So we're
going to copy this, paste it in here New File IO ERC 20 dot som
paste and now we have an ABI for ERC. 20s So we'll do get
contract that ERC 20 Actually let's change this name to ERC.
20 address so we're going to get the contract with the ABI of IRC
ERC 20 at contract address ERC 20 address and then we'll
connect it to our account here. So and once we have the ERC 20
We can do const TX, we're going to do t
hat approve transaction
we'll do a weight ERC 20 token dot approve spender address, and
then amount amount to spend. We'll do we'll do a wait TX dot
wait for one block. And we'll do a little console dot log saying
approved. Now if you don't run this function before you try to
deposit you'll just get an error saying hey, token is not
approved, which is a pretty common error. So if you ever see
that just know, I forgot to approve my token. So backup in
our main script, we'll go ahead and run this
function. We'll say
await approve ERC 20 with the Web Token address lending pool
dot address because we want to give the len pool the approval
to pull our web token from our account. And then we'll give it
some amount to actually will import a mount from get weath as
well. Mount we gotta go back to get with an export it so we can
actually import it. So get with exporting that amount that 0.02.
So we'll prove the amount and then we'll connect we'll have
our Deployer do it obviously because we're
doing everything
with the Deployer. So we'll approve the ERC 20. And then
once we approve, we can go ahead and deposit it. So we'll say
console dot log, depositing that. And then we'll run await
lending pool dot deposit. If we look at the deposit function, we
can see all the parameters that the deposit function takes. We
can also see it in the Ave v2 documentation and we just look
forward the posit. And we can see it takes the address of the
asset that we're going to deposit how much of that ass
et
we're going to deposit address on behalf of we're going to do
it on behalf of ourself and then a referral code, which right now
is just always going to be zero because the referral code has
been discontinued. So we're going to deposit the wet token
address, we're going to deposit our web token, we're going to
posit 0.02 of that web token. And then we're going to use a
deployer ash and then referral code is going to be zero. And
then we'll do a little console dot log the positive. So let's
try
this script. Let's see if it works. I'm just going to hit up
to go ahead and rerun this script to rerun this command I
just ran into doing a little compiling that ERC 20. That song
was compiled one solidity file, got a bunch of wrapped Aetherium
lending pool address. We approved it, we deposit we were
depositing it and then we deposited it. So if we're
looking at our little readme here, we get a little checkmark. We've done step one, we've
deposited our collateral. Awesome. So now we have some
collateral to use to borrow other assets. Great. So now
we've deposited let's go ahead and learn how to borrow now
through a couple of new lines, and we'll say borrowed time. So
in order for us to borrow, we probably want to know how much
we can borrow. And we want to know more about our account,
right? We want to know how much we have borrowed, how much we
have in collateral and how much we can borrow. So there's a
function that Avi comes with called Get User account data,
which will return the
user's account data across all
reserves, how much collateral we have down the total value it in
its eath price, we have the available borrows and eath
current liquidation threshold, loan to value etc. Now these are
really important metrics. If we have one, eath and collateral
that doesn't mean we can borrow one eath of assets. Each one of
these tokens have some different values like loan to value. For
example, if you have one eath you can only borrow zero point
75 for the dye token, this is to
reduce risk of the collateral
and reduce risk of people not having enough collateral down as
prices fluctuate. There's a liquidation threshold of 80% if
you have one eath as collateral and point 81 eath borrowed,
you'll get what's called liquidated. So what is
liquidation? When you put down collateral and you borrow, if
the amount that you have borrowed past this liquidation
threshold is passed that 80% Or, or depending on different
assets, it's different people can do what's called liquidate
yo
u. This is when they pay back some of your loan that you took
out. And they also get to buy some of your collateral at a
cheaper price. This keeps the Ave platform solvent, and it
makes it so that there's never more borrows than there are
collateral in order to borrow assets, we still need that
collateral down. So basically, if you borrowed more money than
you've put up, other users can can take the money that you've
put up in return for them paying for your loans. So we obviously
don't want thi
s to happen. And the audit protocol
programmatically doesn't want to have not enough money to do
this. So they incentivize users to liquidate, in case of these
failures is the protocols come with this thing called a health
factor, which if this health factor is below one, you go
ahead and you get liquidated, the actual function to liquidate
somebody is called liquidation call. So you can actually build
a bot and you can liquidate users who go insolvent and you
can make a fee, you can make a rewa
rd for actually doing this.
These protocols need to stay solvent, they need to have
enough money to lend out. And they programmatically enforced
this, which is why it's so great. You can learn more about
liquidations in the liquidation documentation. So this get us
account data will tell us how much we have collateral, how
much we have in debt, and how much we have available to borrow
based on how much collateral we have. We can see the current
liquidation threshold we can see the loan to value
and then we
can see our health factor, which is obviously really important.
If our health factor ever falls below one, we get liquidated. So
we never want this hell factor to fall below one when we're
borrowing assets. So let's create a function that can grab
that first. So let's create a new function called async.
Function get it borrow user data, and we'll pass in the
lending pool contract, we'll pass in the lending pool
contract and the account that we want to get the data for. So we
can say
const. And actually, we can pull out just the values
that we want, we could pull out the total collateral eath total
debt eath and the available to borrow. Let's just pull out the
total collateral eath the total debt if and the available,
borrows. So this equals await, lending pool dot get user
account data of account. And now we'll even just kind of logged
us out we'll say console dot log you have total collateral eath
worth of eath the positive console. Log you have total debt
eath worth of ea
th borrowed and then console dot log you can
borrow available borrows eath worth of
eath. And then we'll just return available to borrow will turn
available borrows eath. And we'll return or total debt, we
don't really need to return total collateral we could if we
want we really just want to print it out here. So now back
in our function, we can do in our main function, we can run
let, I'm going to do that because we're going to be
calling this a few times available borrows eath. And
total debt
eath equals await, get borrow user
data of lending pool and Deployer. And if we run this,
we'll see how much we can actually borrow yarn hard hat or
just h h run scripts on a borrowed.js and work on our
forked blockchain here. And remember, it is going to be a
little bit slower. And this is kind of one of the disadvantages
too because it does have to make API calls whenever we want to
interact with these chains. And then we got total collateral
eath is not defined. And that's because I spelt to
tal wrong. So
let's spell total correctly. And we'll run this again. But okay,
great. So you have this month's worth of eath deposited, you
have zero worth of eath borrowed because we haven't borrowed
anything, and you can borrow this much worth of eath.
Remember, the amount that we can borrow is always going to be
less than the total amount that we have as deposited. That's why
we see this lower number here. So cool. So that's how much we
can borrow. Let's use that to go and borrow some time. S
o we have
this total amount we can borrow an eighth and we're gonna get to
borrow time. I promise we're gonna get to borrow time, but we
need to figure out What's the conversion rate of diets? We're
gonna get how much we can borrow in eath. But we want to borrow
dye. So how much of dye can we borrow based off of the value of
eath? And to do that, we're gonna have to get the dye price.
And how are we going to do that? Well, you guessed it, we're
going to use chain link price feeds. If you look in
the AVI
documentation, you can find price Oracle, which is a
contract that you could actually use right directly from Avi. But
the first thing it does is check from a chain link aggregator,
which we already know how to do. So we're gonna go ahead and just
call directly from the chain link aggregator. So let's create
a new function function, we'll call it get dye price is first
we're going to need to get that interface, same thing. So you
can either go right to Hardhead defy and just grab the in
terface
right from here, we could swap this out with just an import
from chainlink NPM, as well. But I'm just going to go ahead and
copy paste a new file, this is going to be the Ag reg gate, Tor
v3 interface. Dot saw that we've worked with so many times. Now
that we have this interface, this will compile, we're
obviously looking for latest round data, which will give us
this answer here, which is going to be the price. So let's go
ahead and grab that. So we'll say const di eath price feed
equal
s await ethers dot get contract at and we'll use the
air, Greg a Tor v3 interface will get the dye eath price feed
right from the chain link Docs. So we go to docs dot chain dot
link UVM chains will go to contract addresses on Aetherium.
Or look for di eath on Main net. And we see di eath is right
here. So we'll grab this and again, we're just hard coding it
in the GitHub repo associated with it. With this, we put it in
a little config file, but we can just go ahead and hard coded in.
And for th
is one, we don't need to connect this to the deployer
account. Since we're not going to be sending any transactions,
we're just going to be reading from this contract, right. So
reading don't need a signer sending me to signer. Now we can
say const price equals a weight dye eath price feed dot latest
round data. Now latest round data, as we know is going to
return us this huge thing. And we only want the answer at the
first index. So another way we could do this, we could just
wrap this whole th
ing up. And then once this returns, we're
gonna say okay, just grab that first index here, which will be
that price. And then we could do a little console dot log, the dy
eath. Price is in price dot two, like that, and then return
price. So we can go ahead and run this as well test this out,
just by hitting up and then enter. Depositing we deposited
this is how much we can deposit. Nothing Oh, and I forgot to call
it oh, excuse me, let's go up. This, let's say const dye price
equals await get dy
e price. And tada. The dye youth price is
this big number, which of course we know is going to be $3,289
which is which of course is gonna be 3289 di per eath. Now
that we have the dye price, we can figure out how much dye we
want to borrow. So great, we have the price. Now let's figure
out the amount that we can borrow in dye, we have the
amount we can borrow eath, we need to convert it to die. So
we'll say const amount die to borrow equals available, be the
available borrows in eath.to string.
And then in JavaScript,
we can do this.to string but still do math. So times 0.95
times and then we'll do the reciprocal of that die. So one
divided by die price.to number. So this will give us the amount
of die that we can borrow. And then we'll want to get this in
way. So if we print this out right now, console dot log, you
can borrow. Run this now, you can borrow
amount die to borrow. Not in way units, which we need in way
units, but you can borrow 48 Die, which based off the price
looks abo
ut right. So to get the correct units, we'll say const
amount, di to borrow way. That's going to equal ethers dot utils
dot parse ether, MT di to borrow.to string. This is just
purely the amount of dye to borrow right so we get 48.79
die. But again, we want that in way the die token has 18 decimal
places similar to Aetherium. So we need that amount in way and
then we can go ahead and start actually borrowing now. So we'll
create a new function called borrow die async function,
borrow die will ta
ke the die address. Take the lending pool,
take the amount die to borrow and weigh in Then of course,
we'll take the account. And all we'll do is we'll do const.
Borrow TX is going to be a weight, lending pool dot borrow,
die address, amount die to borrow. And again, we can go
right to the documentation, if we want, it takes the address of
the asset, the amount, we want to borrow the interest rate
mode, which is going to be variable or stable, the referral
code and then address on behalf of, we'
re going to say one for
the interest rate mode, where one is going to be stable. And
then we're going to do zero or this referral code because
that's debunked now, and then we'll do a count, then we'll do
a wait, borrow TX dot Wait, wait one transaction, and then we'll
do console dot log you've borrowed. Well, and that's it.
So we now have this borrowed I function. So back up in our main
function, right, now, we can finally do the borrow time. So
we'll do a wait, borrow die. And we'll pass those
parameters in
here. So we're going to do const, die token address, equals
and we're just going to hard code this from main net. So we
can look up die token address, main net, we'll grab this
address here, this looks like this is indeed the DI token, we
check right on the object get up, we could check right on the
ABI of actual code, since we're just testing, we're just gonna
go ahead and grab from ether scan here. So for borrow dye,
we're gonna need the dye token address, we're gonna need the
l
ending pool contract the amount of dye to borrow in way, and
then our Deployer. And then we'll await borrow dye. And then
we will run this get bar user data again, just print out the
information about where we are after we do that. So when we can
run this, again, we should see the amount that we have
borrowed, updated, our first call to that function is gonna
say, Hey, you have this much eat the positive Do you have nothing
borrowed, you can borrow this much eath, we get the price, we
get how mu
ch we borrowed, we borrow. And now it says you have
this much where the eath deposited and you have this much
worth of eath borrowed and you can borrow this much eat. So we
actually now have bought a bunch of this dye actually borrowed.
And the reason we're doing times zero point 95, we don't want to
hit that cap of the maximum amount that we can borrow. So
we're saying hey, let's get 95% of the amount that we actually
can borrow. So we're not going to borrow everything, we're just
gonna borrow
95%. And you can see that the amount of eath we
have deposited is actually higher. This is because we're
actually gaining interest just by having this eath deposited.
And now that we have some dye borrowed, we borrowed 48 dye,
which is equivalent to this much Aetherium. And then we still
have a little bit more we can borrow because we only borrowed
95%, which is great. Awesome, we've taken out a
borrow programmatically, let's repay at least some of it here.
So we're gonna have to create a new fu
nction that's going to use
the repay function in the contract. So we're gonna do
async function repay, and this is going to take the amount that
we want to repay the dye address that we're going to repay the
lending pool and then the account now to repay once again,
we're going to have to approve sending our dye back to Ave So
in here, the first thing we need to do is we actually need to
call wait prove ERC 20 with the dye address, lending pool dot
address, mount and then accounts, right because
approve
ERC 20. That's the input parameters it takes, and we need
to approve sending the dye back to the contract. So we borrowed
it, and we're gonna send it back. Now we're actually going
to send it back. So let's say const, repay TX equals await,
lending, cool that repay the address, amount, one account.
And then we'll say await repay TX dot Wait, that will do
console dot log repaid up in our main function, we're going to do
a weight repay, and we're going to give it the amount dye to
borrow
in way. We're gonna give it the dye token address, give
it lending pool, and then we'll give it deployer get you borrow
user data one more time just so we can print out the final
amounts. Now, you'll notice something though, we're going to
give back all of the dye that we borrowed. However, we're still
going to have a dye balance, you'll see that when I run this
that we're still have a little bit of Aetherium borrowed,
basically, we'll still have a dye balance because we'll still
have a little b
it of die borrow and try to figure out why before
I answer it actually. So we have this tiny, tiny tiny amount of
eath borrowed here and we have a much larger amount of eath
deposited so Why do we still have this tiny, tiny amount of
eath borrowed? Well, the reason is because as we borrow dye, we
actually accrued interest. So we still owe dye back. Now what we
can do is we can do something like uniswap, to actually swap
our Aetherium, for dye to repay the rest of our debt here. And
that's how we
could actually finish repaying all the debt is
to get a little bit more dye to pay off that interest that we
had accrued. And if you want, you can go back and you can do
the exact same thing we did here to grab the uniswap code, place
it in here to programmatically repay your debt as well. But at
this point, you have just gone through the entire lifecycle
here. And that is absolutely massive, huge congratulations.
You've just deposited borrowed and repaid tokens from the AVI
protocol. Now I'm g
oing to go briefly show you what some of
these transactions are going to look like on an actual test net,
on an ether scan, you'll see that when we deposit our
collateral, we actually get back what's called an A token, or an
interest bearing token, these tokens keep track of how much
collateral or in our case how much web token we have deposited
in the AVI protocol. And when we want to withdraw our wealth
back, we burn these eight tokens, we remove these eight
tokens, you can see that our first
transaction is going to be
deposit and I wanted to show you what it looks like when you
actually deposit one of these tokens, this transaction
associated with this lending pool, that deposit right here,
if you look at tokens transfer down here, you can see we
actually we deposited, you can see that we actually sent rapt
ether to the ARB a contract. Now you'll also see this a weath
stuff here. So what is this a with stuff. So to keep track of
how much you've actually deposited into Ave, Ave will
give you your own a wealth token or a token to keep track. And
this is this interest bearing token, you actually can see up
here a little bit of interest already for actually depositing
these tokens into the protocol. And it's this token, that will
keep going up, the more people borrow and the more people use
the protocol. So you can actually grab this token
address, interest bearing token this a token, I can import it
into my meta mask. And I can see that I have 0.1, which
represents my initial
deposit 00517 dot that dot A with and if
you keep this up long enough, this number will slowly go up as
you get more and more. Because the interest keep out and you
can see just went went up right there, the interest will keep
changing and keep going up. Obviously, we have so little
deposited that the interest isn't gonna go up very quickly,
but it will go up, the more people use the protocol. And
since I'm using a test net, the actual usage of the protocol
isn't very high, but you get the pict
ure. Awesome, you've done phenomenal
to get this far. Let's do a quick recap of everything that
we've learned. And then we'll head on to the next section. So
first, we learned about the web token or the wrapped Aetherium
token, it's a way to tokenize our Aetherium or layer one
blockchain native token. Then we learned a little bit about the
AVI protocol, and how it's this defy primitive for borrowing and
lending assets. And we can actually gain interest by
depositing our tokens and our assets int
o Ave we learned a
little bit of also about uniswap, which is another
incredibly important defy protocol, which allows us to
swap tokens between each other in the decentralized context,
then we learned that we can actually deposit some of our
tokens into the AVI protocol, which is a decentralized
borrowing and lending platform and similar to a bank will
actually gain interest on our deposited tokens. But first, we
have to approve them. Because anytime you want a contract to
interact with your to
kens, you need to approve the contract to
do so. And then we go ahead and deposit once we deposited we got
the dye price. And then we learned that we can actually
borrow dye we can borrow an asset based off of how much
collateral we put down. And then we learned how to repay it back.
We learned about forking a blockchain as opposed to using
our own main blockchain. Another thing to know if you are using
an RPC URL like something from alchemy so awesome you've
learned a ton about defy now defi is
an absolutely massive
powerhouse when it comes to the blockchain. And it is one of the
most important things blockchains can do. If you want
to learn more about defy and read more on Defy. I've got some
more links in the GitHub here so that you can learn more about
defy and one of them in particular that I want to show
you is this one called speed run Etherium. Not only does it give
you a ton of defy examples, but it gives you a whole bunch of
other examples as well. And this will be a good tes
t of
everything that you've learned after you pass this course. Or
even right now if you want to or whenever you want and to talk
about it a little bit more, we actually have Austin Griffiths
here to talk a little bit more about speed run Aetherium
himself, I'll pass it over to Austin. Hey, what's up, I'm Austin
Griffith, I want to show you speed run Aetherium. speed run
Aetherium is a great way to get started in Aetherium. If you are
a developer, it's targeted at web two developers becoming web
three developers speed run a theory. m.com is the website. It
takes you through both getting started and kind of getting
getting an idea for the language and the syntax. But that's just
the start. Just understanding the language is just the start,
you feel like you're you can do anything. And you're on the top
of the world when you finally get the syntax of solidity
together, and you can jam through a smart contract. But
really getting context with the space and figuring out what
works and what
doesn't, that's a whole nother battle. And that's
where speed run aetherium.com comes in. So let's speed run the
speed run. First, you will get scaffold e down speedrun
Aetherium kind of revolves around scaffold eath and uses
scaffold eath as a base, you'll want to tinker around with some
ideas within solidity. And let me show you what I mean by that.
So with scaffold eath, you have a front end and you have your
smart contract. So scaffold eath comes with hard hat out of the
box. And you will u
se the combination of hard hat and
react to build a DAP where you'll deploy both the smart
contract and the front end. And this, this ability to edit your
smart contract and have your front end auto adapt to it is
kind of the key to scaffold ease. I just added some extra
extra exclamation points. But we'll see that show up over
here. Once this contract deploys, there we go, there's
that. So just real quick, again, if I create like a un 256 public
counter, and we set that equal to five, and then
I build a
function called increment, that's public that does counter
public, there we go, that does counter plus, plus, you can
imagine what's going to happen here. So you, you edit a little
bit of solidity, you deploy your contract, and then your front
end auto adjust to that. And it gives you the ability as a
developer to call those functions tinker with your smart
contract to play around, you can even have a console log in
there, where it you know, sets says the count now is there we
go somet
hing like this. And let's go ahead and deploy that.
Notice I'm doing these quick iterations, I'm making small
changes in solidity, and I'm seeing those changes show up in
the front end. And I'm tinkering with those in the front end and
testing my assumptions. Here, if we go look at our hard hat node,
when I make this increment call, we should see that nice console
log there, you know, this address, set the count to eight,
right? Very, very cool. So this is scaffold eath. This is what
speed run A
etherium is built on top of, you'll get in here,
you'll edit your smart contract, then you'll edit your front end,
you'll point your front end at some particular network, you'll
deploy your smart contract, you'll deploy your app, it just
gives you the ability to have a front end along with your smart
contract. And you'll have that as you're building your smart,
smart contract. So to have this front end, to tinker with your
smart contract is going to help you kind of figure out how you
want to wr
ite your solidity, like, is this going to be a
mapping? Is this going to be an array? How am I going to track
this struct, you can kind of do this in an iterative process by
just throwing it in here into your smart contract and
tinkering with it on the front end and trying it out and seeing
what you need to build. So that's scaffold eath. And that's
the base that you'll need to get started with speed run Ethereum.
Once you've you're able to have this all installed, you'll
you'll have your kind o
f react front end here with yarn start,
you'll have yarn chain, which will run your your hard hat
node, then you'll do yarn deploy. Once you have that setup
locally, you're ready to go with speed run Aetherium. And you can
also do this right here and challenge zero. So challenge
zero, gotta go zero index, right, we got to be nerdy
challenge zero sets you up with just getting the environment
setup, you'll you'll quest on building a simple NFT example.
It's going to come with an NFT smart contract
. And it's going
to come with an NFT front end a little kind of like minting
view, it'll come with all of this stuff to do that. And it'll
walk you through basically challenge the row is going to
hold your hand, it's going to take you through every step
you'll have to get get you have to have a certain version of
node E, L some some correct range of node and yarn
installed. Watch out yarn has an executable on Linux, make sure
you have the yarn, the package manager. But after you have get
your no
de and yarn, you'll run through cloning down each
challenge. You'll do an install, you'll fire up the chain and
you'll fire up your front end. And you'll have a working app
that lets you interface with your NFT smart contract. You'll
go through here and you'll learn about wallets. Then you'll start
minting NF Ts and you'll send those NF T's are Round. And
that's challenge zero, it's just getting you started. Here even
in challenge zero, you'll deploy this NFT to Rinkeby. And you'll
also deploy a
n app and allow your friends to go to your app
and mint in FTS on Rinkeby. So that's the first challenge, it
looks like we even like dive into open see and play around
with some of those mechanics. The second challenge challenge
number one challenge one of speed runner theory is like
where it all really starts. This really shows off the superpower
of Aetherium. It sets it up. So you need to build an app where a
bunch of people that don't necessarily trust each other,
can coordinate and stake int
o a smart contract. And this is
like, this is the superpower of Aetherium. The ability for you,
the developer to write a few simple rules to allow jerks to
coordinate financially, and not greet each other and steal each
other's money, right? You're we're building these financial
systems, there's, you know, game theory and economics and so many
other things going on here. But you as the developer, you're
writing simple rules, and you're building a system that allows
people to coordinate. So chall
enge one, we'll take you
through how to get set up with your steak or docile, smart
contract, you'll install everything exactly the same way,
you'll fire everything up. And then it's just going to walk you
through the kinds of things that you will need in your smart
contract. But you'll have to write the solidity yourself. So
this is this is not going to be a handheld tutorial, hell thing,
you're going to have to write the smart contract yourself. And
there's some guidelines and some rails that
kind of help you out,
but it's not going to do it for you. Okay, so that's, that's
Quest One is or that's challenge. One is building a
decentralized staking app. Then challenge two is building a
token vendor this so in challenge one, you'll learn
things like how to send money into a contract, how to have a
contract, keep track of mappings. In challenge two,
you're going to learn contract to contract interaction, you're
going to learn about ERC 20s, you're going to learn about
specifically the ap
proved pattern, which is kind of a
jerk, it's, it's hard, it's a hard thing to deal with is the
approved pattern, you need to go to your token contract and
approve the vendor to take some money, then in a second
transaction, you need to go to the vendor and have the vendor
grab the money from the token contract and do something else.
So that's that's challenge two is learning about tokens and
vendors, and is starting to really like get you kind of
exposed to the idea of this like massive multipl
ayer game that is
Aetherium. And also kind of like how to build these vending
machines that anybody can get to, then you'll build a Dex. Now
once you've once you're done with 01. And two, you're really
kind of you have a license to learn at this point, you're
you're ready to really like go do some damage and build some
cool things, maybe go build a couple other things, but come
back and come come hit challenge three, challenge three is going
to be a little bit more open ended, there's going to b
e a
cohort of other people that are also building this. And you have
to get through the first three challenges to even get to
challenge three to even get to this chat room. But there's a
chat room where other developers that are also building their own
decks are all there together, and you can kind of learn with
them and chat with them. But you're going to build an
exchange. And you're going to build an exchange that works in
a smart contract in a decentralized way with no
centralized breaking p
oints. And what that's going to mean is
you're going to have to have reserves of both eath and tokens
and you're gonna have to have a pricing function and LP tokens
and all sorts of other things that you'll have to learn about
as you get to it. Then challenge five is a multi SIG wallet. Such
a fundamental important thing about how to store your eath
safely is going to be in a multi SIG wallet and how you can have
multiple identities even even like the the base of like what a
Dow is, is sort of l
ike starting with a multi SIG wallet. And
you'll need to build one of those you'll need to understand
call data call data is super weird and complex. And
everything's a transaction even when you're just poking a
contract. It's a transaction, and you have to craft that call
data correctly to say, I would like to call this specific
function on this specific contract. So that's multisig
wallets and then it kind of ends up we've got more challenges in
the pipeline, but it kind of ends on build an SV
G NFT so much
fun to build an NF t that crafts the actual drawing in the smart
contract and renders it. So that's the speed run, go speed
run Aetherium checkout scaffold, eat start building, build
something awesome on Aetherium. hearts, hearts, hearts, go get
them. Austin Griffith is one of the
OGS when it comes to helping engineers get caught up and up
to speed in the blockchain world so massive thank you to Austin
for all he's done so far. All right now we're gonna get
into NF T's. Let's do th
is. Now, I've already made a number of
videos on what NF T's are and how to start working with NF
T's. So we're going to watch a portion of the previous Python
edition of this course where I explain and a piece from a high
level and then of course, we're gonna get into the ultimate NFT
tutorial. So let's learn about NF T's look, NF T's are hot
right now. Nf T's also known as ERC 720 ones are a token
standard that was created on the Ethereum platform. Nf T stands
for non fungible token is a token
standard similar to the
ERC 20. Again, ERC 20 is like link of a maker, all those
goodies that are found on the Etherium chain. An NF T or a non
fungible token is a token that is non fungible, this means that
they are starkly unique from each other. And one token isn't
interchangeable with any other token of its class. A good way
to think about it is $1 is interchangeable with any other
dollar $1 is going to have the same value of another dollar.
Those are fungible tokens that's like ERC 20s one
link is always
going to be equivalent to one other link, by contrast is going
to be NF T's those of you nerds out there, we know like a
Pokemon would be a good example of an NFT you're one Pokemon is
going to have different stats, different moves, sets, and isn't
interchangeable with any other Pokemon, or maybe a more
relatable one. It's like a trading card, or unique piece of
art or the like. So that's what these NF T's are there non
fungible, non interchangeable tokens that for the moment are
best represented or thought about as digital pieces of art
that are incorruptible and have a permanent history of whose own
them who's deployed them, etc. Now, like I said, NF T's are
just a token standard. So you can actually make them do much
more than just be art. You can give them stats, you can make
them battle, you can do really unique things with them, you can
do pretty much whatever you want with them. But right now, the
easiest way to think about it, and the most popular way to
think a
bout it is by calling them ah, ah, ah it's odd, or
some type of collectible or just anything that's unique. Now,
they've been getting a ton of buzz recently, because we've
been seeing more and more of these being sold at insane
prices, like we saw x infinity sell nine plots of their land
nine plots of their unique land for $1.5 million. We also saw
the original creator of the Nyan Cat, you know, this cat sold for like 300 eath. So
apparently people really value these things. So like I said,
they
're just tokens that are deployed on a smart contract
platform, and you can view them on different NFT platforms like
open sea or wearable. And these are the NFT marketplaces that
let people buy and sell them. You obviously can do that
without these marketplaces, because it's a decentralized but
they're helping give a good user interface. Now, like many of you
out there, my initial thought to NF T's was okay, this sounds
pretty dumb. But I think that that was dumb. I think art does
have a lot of
value. And I think that artists are not always paid
fairly for what they do. And this is actually a huge issue
right now in the modern day world where an artist can make
some type of art people just copy paste it, you know,
everywhere and, and they never get attribution for what they
make. So having a really easy decentralized royalty mechanism,
or some type of mechanism where these artists can get accurately
comped for what they're doing, I think, is really important. I
love music. I love movi
es, those are pieces of art that I digest.
And I really like and I think it's fair for them to get comped
appropriately because they are providing value to my life. I
think NF T's are a great way to solve this issue as kind of
having these decentralized audit trails and, and royalty trails
that we can set up and, and see really transparently without
having to go through some centralized service. So that's
the basic gist of it. Let's talk some more about the standards
ERC 721 standard are the NFT
standard. This is the basis of
it all there is another standard that's semi fungible tokens, the
1155 We're not going to talk about that here. But you can
check it out the main differences between a 721 and
the ERC 20 and ERC. 20 is they have a really simple mapping
between an address and how much that address holds. 720 ones
have unique token IDs, each token ID has a unique owner. And
in addition, they have what's called a token URI, which we'll
talk about in a minute. Each token is unique. Ea
ch token ID
represents a unique asset. So since these assets are unique,
and we want to be able to visualize them and show what
they actually look like we need to define those attributes of
the object. If it's a piece of art, we needed a way to define
what that art looks like. If it's some type of character in a
game, we need to wait define that character stats in the NFT.
This is where metadata and token URI has come in. So if you know
anything about Aetherium, you know that sometimes gas price
s
getting pretty high, especially when it comes to storing a lot
of space, it can get really, really expensive. So one of your
first questions might be Well, are they storing these images?
And these are pieces on chain? And the answer is sometimes back
when they were coming up with NF T's and artists were deploying
stuff, the eath devs and the artists were like yeah, art,
let's do that art. I'm just gonna deploy This one megabyte
image onto the Etherium chain. And Oh god, it's so much gas
expens
ive, delete button. Otherwise, it's not, it's not
good. And they realized that if they put all this art on chain,
it's going to be incredibly expensive. So to get around
this, what they did is they put in the standard, what's called
the token URI. This is a universally unique indicator of
what that asset or what that token looks like and what the
attributes of the token are, you can use something like a
centralized API, or IPFS. To actually get that token URI.
Typical token URI has to return som
ething in this format like
this, where it has the name, the image, location, the
description, and then any attributes below, there is often
this talk of on chain metadata versus off chain metadata.
Because it is so much easier and cheaper to store all your
metadata off chain, a lot of people will use something like
IPFS that is decentralized, but does take a little bit of
centrality to keep persisting, but they can also use their own
centralized API. However, obviously, if that goes down,
then y
ou lose your image, you lose everything associated with
your NFT. Because of this most NFT marketplaces actually can't
and won't read off on chain attributes or on chain metadata,
because they're so used to looking for the token URI.
Obviously, if you do off chain metadata, you can't do anything
really cool or really interesting or have any gains
with your NF T's. For example, if you wanted to create an on
chain Pokemon game, all your attributes would need to be on
chain in order for your Pokemo
n to interact with each other.
Because if it was off chain, then that becomes a lot harder
to cryptographically prove. So if you're new with NF T's and
you're like wait, this is kind of a lot of information, I'll
make it easy for you. If you're looking to render an image, I'm
an NFT, add your image to IPFS, add a metadata file pointing to
that image file on IPFS. And then grab that token URI and put
it and set it as your NFT. The chain link DND article does a
great job of walking you through thi
s and showing you how to do
this. So be sure to read that if you're looking to learn how to
do that we're not going to cover that in this video, but we will
be deploying our first NFT with some on chain attributes. Again,
having your attributes on chain is really going to allow you to
build really creative NF T's that build games or have
interesting properties and and really makes the authenticity of
your NFT guaranteed because those attributes are always
going to be on chain. Alright, so now th
at we know the
basics of approximately what an NF t is, and similar to the ERC
20 You can see the E IP 721 or the ERC 721 non fungible token
standard on the Aetherium, ie IPs. And once again, if you
scroll down, you can see all the different events and the
different functions that come with creating this token. And
now everything that we're going to do is going to be available
at this GitHub repo. This hard hat NFT FCC, we're going to
actually go through all the code down to deploying and creati
ng
our own customized NFT. And I've labeled this the ultimate NFT
repo as part of this course, because we're going to go
through a lot here, we're gonna go through a basic NFT, a real
minimalistic NFT and then an IPFS hosted NFT. That is
dynamic. And it uses randomness to generate unique NF T's so
that we can have provably rare and appease or provably rare
cards or provably rare tokens or stats or whatever you want. And
then we're going to do what's called an SVG NFT. These are
entities that are
100 percent hosted on chain, so you don't need an off
chain. So you don't need IPFS, you don't need an off chain
database. And this one's also going to be dynamic, where it's
going to use price feeds in order to fluctuate what the
image of the entity actually looks like based off the price
of some asset. And here are the images that we're going to be
using. And we obviously have these three adorable doggies
here. If you want to follow along with the Quickstart, you
absolutely can. And I'm going
to do a quick overview of just
running the code to show you what it's going to look like at
the end. Basically, what we're going to do is we're going to
have our code, we're going to run hardhat deploy dash dash
network, rink B, dash dash tags main. And this is going to
deploy all of our contracts and everything. And then finally, if
we go to test nets dot open c.io And we grab the address of one
of these NF T's we should be able to put it in here see our
actual NFT as a collection with an item
. Or Additionally, we can
just go right to the contract. We can read the contract, we can
get the token URI, and then we can copy this token here, I
paste it into our browser or any IPFS is into our browser, and
then grab the image attribute and see what this actually looks
like on chain. So with all that being said, let's learn how to
build this ultimate NFT repo and build all of these different
customizable NF T's let's jump in. So once again, we're in our
terminal I'm going to create a new di
rectory. We're gonna call
it hard hat and a T for FCC. We're gonna cd into that. And if
T FCC and we're going to open that up with code.or, you can
hit File, Open Folder At this point, you'll have gotten pretty
familiar with the setup of our code bases here, feel free to
copy over or do whatever you want to do for our setup, we'll
do yarn, add dash dash dev hard hat. And then while that's
loading, I'm going to copy over my prettier files. Because I
want to use prettier, I'm going to copy over my
hard
hat.config.js. And I'm also going to grab my package that
Jason copied over here, I'm going to delete this old package
dot JSON. And I'm just going to hit Enter on this and rename the
one I just copied over from package copy to package json.
And the reason I'm doing this is so that I can just go ahead and
run yarn and install all this stuff for me. Or we can just
come back over here and just grab this lesson nine hard hat,
smart contract lottery, all that stuff, again, just run that
massiv
e piece as well. Or you can copy over your package json,
and then just run yarn that will do the same thing. Now we don't
have to keep doing this boilerplate over and over and
over again. And then while that's loading, I'm also going
to grab my Dotty and v file that we've been using on a past
couple of projects, the readme.md. And we'll just do
what we're gonna be doing here. So we're going to make three
different contracts. One is going to be a basic NFT, using
that ERC 721 standard, then we're
gonna do a random IPFS
hosted NF t. And then finally, we're going to do a dynamic S V
G NFT. So our random NF T is going to be random at creation
time, this is going to give some true scarcity and some true
randomness to our NF T. And it's gonna be hosted on IPFS. Our
dynamic SVG NFT is going to be hosted 100 percent on chain, and the
image of it's going to change based off of some parameters.
That's what makes it a dynamic SVG NFT. Let's go ahead and we'll create
a new folder, we'll create our
contracts folder, and we will
create our first NF t this is going to be our basic NFT. And
I'm going to go a little bit quick here, because most of what
we're going to be doing is actually things that we're
already familiar with. So we'll do basic NF T dot soul in a new
file. And let's go ahead and let's do it. Let's go ahead and
we'll do spdx license identifier, it's gonna be MIT.
We'll do pragma, solidity, carrot zero, point 8.7 pragma,
solidity, and then we'll do contract, basic NF t. Let me
just basic, get a t.so, like that. And we'll say contract
basic NF t. And then we'll just run hard at compile or yarn
hard, hit compile, and it looks like we're doing well here so
far. Okay, perfect. Based off of that NFT token standard, we go
back that EIP, we're going to need a whole bunch of different
functions here, we're going to need transfer events, we're
going to need owner events bounce up, we're going to all
these different functions. And we could 100 percent implement these
and trans
fer them exactly like the ERC 20 did, or once again,
so we can use, you guessed it, we're gonna be using open
Zeppelin contracts for this as well. So we come back over to
open Zeppelin, we go over to contracts. And we're gonna go
ahead and add this with yarn, add dash dash Dev. So yarn, add
dash dash dev at opens up on contracts, like so. And while
that's going through, we can look at the ERC 721 of this and
we can see what creating a minimalist ERC 721 looks like.
Now there's a number of extens
ions that come with its
ERC. 721 In this example that they give us. They're using ERC
721 Uri storage, which we'll talk about in a little bit. So
we've added it in here. And now we can actually import that ERC
21 from opens up when in our contract. So we'll do import at
open Zeppelin slash contracts slash token slash your C 721
slash ERC. 721 does so they use a different one in the demo, but
don't worry about that. And same as the ERC. 20 we're gonna say
our basic NFT is ERC 721. So we're doing
this inheritance,
you find the constructor, we can see this has a constructor,
where it takes a name and a symbol. So we're gonna want to
use this constructor and our contract. So we're gonna say,
construct door. And our constructor is just
going to be blank. But we'll do the ERC 721 constructor and
we'll call this a doggie. And the symbol will be dog just like
that. Our basic NFT we're just going to have it be this doggy
here, right so it's going to be an NF T of just a purely this
dog here. Th
e name is going to be doggy and the symbol is going
to be dog. Now in order to create new dogs. What we're
going to do is openzeppelin code comes with something called a
mint function exactly the same as the ERC 20 So we're going to
create a function called mint NFT this will be a public file
function that's going to return you activity six. And we'll use
the safe mint function of this ERC 20 or underscore safe mint
message dot sender will mint the token to whoever calls this mint
function. And
then we need to also give this a token ID, if
we're looking back at the code for the ERC 721. And again, you
can see this on GitHub as well, we're gonna look at this safe
mint function, it takes an address to who is going to own
the NFT. And then a token ID, what is the ID of the token
based off of this address. So if you have a collection of tokens
on the same smart contract, each one of them needs their own
unique token ID. So what we're gonna do is we're gonna create a
un 256, private variabl
e called S underscore token counter, and
it's gonna get initialized in our constructor to zero, but
we'll just be explicit and say es token counter equals zero.
And then in here, we'll just have the token ID of this new NF
t be that token counter. And then of course, we'll say s
token counter equals s token counter plus one. So every time
we meant a new NFT, we up the token counter, and then we'll
just return, we'll just return the new tab counter. Right. And
then obviously, at the bottom, we co
uld do like function, get
token counter, this would be a public view returns you in 256.
And I'm gonna go a little quick here, because you've seen this
before, return s underscore token counter, right, since it's
a private variable up here, the public function down here to get
that token counter. So this technically, is it. This is
technically an NF T. But what does this look like? Well, right
now, this NF T isn't going to look like anything at all, in
this EIP token standard. It has this thing
called a token URI.
And this is the important function that tells us exactly
what this token is going to look like. Like what we said in the
mini lesson here. This token URI returns some type of URL or
universal resource identifier that returns some JSON that
looks like this. And in this JSON, we're going to have this
image part. And this image is going to be a URL that's going
to point to what this image actually looks like. Now this
URL can be hosted on chain, it can be hosted on IPFS, it can
be
hosted really wherever. But ideally, we're not going to use
a centralized server to host it. If this is hosted on a Google
Cloud or a centralized server or whatever, and our centralized
server goes down. Well guess what this MC is gonna look like,
it's not gonna look like anything. So we want to use some
type of decentralized storage, to get a URL, or URI to store
what this looks like. To make this section a little bit easier
for you, I've actually already gone ahead and hosted an image
to IP
FS. For you, it's going to be at IPFS dot dot slash slash,
it's going to look like this, this is going to be the image
that we're going to use for our dog here. If you create just
this image, though, as the token URI, that's not going to work,
we need a URI that returns this, with the image inside of it. Now
for this first section, have already gone ahead and done that
for you as well. And that's going to be located here. This
is what our token URI function needs to return. So it's going
to have
the name, the description, the image URI,
which then points to the dog, and then some attributes, etc,
etc. Now, you'll notice that the image here is pointing to HTTPS,
dot dot slash slash ipfs.io. The reason I did this was again,
just in case you didn't have the IPFS gateway, but this would be
a lot better if this was in its IPFS form. Because if the
centralized server ipfs.io, goes ever goes down, this NFT will
show what it'll show nothing. So it would be much better. If it
was instead of ipf
s.io. It was IPFS, colon slash slash, like
that, instead of ipfs.io. But for now, this is what we're
going to be using. And you can just go to the GitHub for this,
just to grab this for this section. Right? Just go to
contracts, basic NFT, you can just grab this, this, this top
part, just copy it like that. So we're going to paste that the
token URI up, up, up at the top, like so. Oh, it should be
returns, return. There we go. Okay, cool. So we're just going
to copy paste that in here like this.
Like I said, even though
the token URI here is pointing directly to IPFS, which is good.
If you actually go to this file, the file is actually pointing to
http.ipfs.io. Again, for your NF T's don't do that for this NFT.
I just did that just in case, but for your NF T's don't do
that. And we're going to make this a public constant variable.
Why? Well, because this token you write is never going to
change. We're going to make this NFT so that everybody who meets
one will have this exact same ador
able little puck here. And
the way we do That now is we need to identify the actual
token URI function. So I'm going to do it above get token
counter, we're going to say function, token URI. And these
always take a UNT 256 token ID, we're going to make this a
public view, override returns string memory. And this needs to
return the token URI. Now, this is going to be the most basic
way to create this, right. And if we wanted to make this
function a little nicer, we'd comment out token ID as well
,
since we're actually not using token ID. But we're overriding
in our in the ERC 721 that we're importing. This has a token ID
function or a token URI function. And we're overriding
this, right, we're not using this at all, we're saying, Hey,
we're just going to use our own here. And that's all we need for
this to work. And now if you were to deploy this to rink B,
if you were to jump over to open C test net, and you would deploy
this, this dog, this adorable little pop would be what shows
up f
or all the minutes. So let's go ahead, and let's create a
little deploy function for this. So we'll do a new folder, deploy
a new file, we'll call this a one deploy basic NF T dot j s.
And this is going to look real similar to everything that we've
done before. So I'm going to move a little bit quicker here.
const network equals require hard hats, const development
chains, equals require dot dot slash helper, hard hats config.
And I don't think I added that. So I'm going to copy paste my
helper
Hardhead config from our last project. And if you get
confused, you can always just come to the GitHub here, and
just go to the helper Hardhead config and grab it from here, we
have that const development chains, or say const verify
equals require dot dot slash utils. Slash verify. We're also
going to grab our utils, I'm going to copy paste so utils
folder from alas project, once again, you can copy paste from
your last project, or you can just go straight to the repo,
grab the verified dot j s,
we're going to go over these two
functions a little bit later, but at least grab the verified
for now. And then we're gonna start the function. So we'll do
module that exports equals async function. We're gonna get get
named accounts, and deployments. And we're gonna say const.
Deploy, log equals deployments against deployer equals await,
get named accounts. So we'll do a little log here just to get
started to do our basic NFT doesn't take any constructor
parameters. So we'll say const args equ
als a little blank here.
Then we'll do const basic NFT equals await, deploy basic NF T,
exactly what we've seen before a number of times at this point,
we'll say from Deployer. args is going to be args. And then log,
it's going to be true. And then we'll also do weight
confirmations. It's going to be network dot config dot block,
confirmations, or one. Then if we want to verify this, we're
going to once again do if it's not development chains, dot
includes network dot name, and process that EMV
dot ether scan
API key will say log verifying dot the dot await, verify basic
NF T dot address arguments which are going to be blank, grab this
little log here, stick it right underneath. And then actually in
our basic NF T. This should be args not arguments. Now we have
a deploy script. Now we have a basic NFT here in test data
deploy script, or any hh deploy or yarn Hardhead deploy. And
oops, let's go back to the basic kind of tea or mint and have tea
needs to return a UNT 256. Try again. Get
token counter also
needs to return a UNT forget you into anywhere else. Nope. Okay,
looking good. And looks like we're compiling well compiled,
deploying Well, now if you want to deploy this to rink B, you
can. I recommend holding off though, because we're going to
do that at the end. Now that we've written a deploy script.
We've written the contract, you already know what's coming next.
Yes, neat. What's next is the tests now because we're not
actually learning too much more here. I'm not going
to walk you
through writing this test. I'm going to challenge you now. How
to pause the video and write the test out yourself and see how
far in this test you can actually get. Remember, to test
it, you're going to want to run yarn, hard hat test, right, let's see if you can
actually write the test yourself, then come back to the
video, see if your tests were just as good or not as good as
what we did here. And of course, you can always go to the GitHub
repo test sections. And if you get lost,
you can look at the
basic nft.test.js To follow along. All right, welcome back.
Hopefully, you wrote some awesome tests, taking the time
to actually do some of those exercises is really going to
make you a much better engineer. Following along with me, it's
awesome. But tinkering yourself and trying to do everything
yourself is really what's gonna make this stick. So I hope you
did pause the video. And hope you did go ahead and try to
write the test for this yourself, you've technically
just cre
ated a really minimalistic NFT. Great job. Let's kick things up
a notch. Let's move now to a random IPFS hosted NFT where
we're going to do everything pretty much programmatically. So
let's jump in. In our contracts, we're going to create a new file
random IPFS, NFT, dot soul. Same thing. spdx license identifier,
gonna be MIT pragma, solidity carrot zero, point 8.7, or
whatever version you want to use, will do contract random
IPFS NF? T, like so. So what is this one going to do? So instead
of ju
st minting any NFT, when we meant an NF t, we will trigger a
chainlink VRF call to get us a random number. Using that
number, we will get get a random NF t that we're going to decide
on. And the random n of t that we're going to use, it's going
to be either a pug a Shiva in you, or a St. Bernard. So
whenever anybody mints NF T, they're gonna get one of these
random three dogs. And we're going to make this so that each
one of these dogs have a different rarity, we're going to
make these dogs rare
by different amounts, say we want
the pug to be super rare, the Shiva to be sort of rare, and
then the St. Bernard to be pretty common. So probably it's
gonna be super rare Shiva in us is gonna be sort of rare, St.
Bernard is going to be pretty common, right? Or the most
common, if you will. So let's go ahead and start building this,
we're probably gonna have to make a function called like
request NF t, because we're going to know that we're gonna
need to kick off a channeling VRF request, we'r
e probably
gonna have to make a function fulfill random words, that's
going to take a un 256 Request ID and a un 256. Array, memory
random words, as we've seen before, we've done fulfill
random words in the past. And let's let's even go one step
further, we'll make it so that users have to pay to mint an NF
t. So this is going to be they have to pay a certain amount of
eath to get the NFT and then the owner of the contract and
withdraw the eath. So we're basically paying the artists
here, we're
paying the artists to create these NF T's. And then
they can be the ones actually actually withdraw the payment
for all these NF T's. And we're also going to need, of course, a
function, token URI, which takes a un 256. And this is once
again, same as our basic NFT gonna be what this token
actually looks like. So let's go ahead and get started creating
this. Now, because these red lines are going to draw me
crazy. We're going to add some visibility here, we'll make this
request NFT public fulfil
l random words, we actually know
from the past is going to be internal token URI is going to
be public. Let's build this request NFT. And again, to
request a random number, go back to EVM. Chains, we go to using
randomness, we can follow along with the Dr. chain link again,
to figure out how to get this random number. So since we know
we're going to be working with chain link, we want to add at
chain link slash contracts. So back in in our code base, we'll
add that in yarn add dash dash dev at c
hain link slash
contracts, like so which was perfect. And we can go ahead,
and we're going to import that VRF consumer base V two and the
VRF coordinator interface into our code because we know we're
going to use both of these. If you want to just copy paste from
the docs, you absolutely can go to pause here. And since we're
going to be using this view of consumer base, we want to
inherit it. We're going to say random IPFS and f t is V RF
consumer base V two and this little, little wiggly line w
ill
show up here saying This seems to be override. So I'm just
gonna go ahead and add override here. And this little wiggly
line is gonna stay there for a little bit until we implement
the rest of the functions. So let's go ahead and implement the
rest of those functions. Request NFT, of course, is going to be
public here. And in order for us to request an NF T, we're going
to need to call the coordinator dot request random words where
we pass all this stuff in. Right, so let's go ahead and get
all this stuff for our V REF coordinator in our constructor,
so let's create a new constructor. struct door. And
we're going to use the VRF consumer base, the two
constructor to use to create our constructor, the VRF consumer
base V two needs an address in here for the VRF consumer base.
So we'll go ahead and we'll do address VRF. Or did Nate Torre b
two. And then we'll pass this to the VRF consumer base
constructor here, just by adding that that red squiggly line has
gone away from me perfect.
And we want to save that address to
a global variable. So we can call request random words on it.
So we're gonna go ahead up here, we're going to say I underscore
var F chord. To me, Tor, we're going to make this immutable VRF
coordinator. And we're going to do it by saying VRF v2
interface, it's gonna be private, immutable, you have
coordinator and then in our constructor here, we're gonna
say IVF coordinator equals VRF, coordinator v2 interface wrapped
around this like so. So we know we're goi
ng to need this, we
know we're actually gonna need a ton of these. So let's just add
all these variables in here, we're gonna need the
coordinator, we're gonna need a UNT 64 private, immutable, I
underscore subscription ID, we're gonna need a bytes 32,
private, immutable, I underscore gas lane, we're
gonna need a you int 32 private, mutable, I underscore callback
gas limit, we're gonna need a UNT 16 Private constants,
request confirmations, we're gonna say it's gonna be three,
and then a un 32.
Private constant equals num words, which
is going to be one, and we'll get this red squiggly line
saying it's mad at our our constructor here. So let's go
ahead and add all of our immutable variables in our
constructor. So we'll get the VRF coordinator v2 from our
constructor will get the UNT 64 subscription ID, we'll get the
bytes 32 Gas lane, aka the key hash, we'll get the un 256.
We'll do a UNT 32 callback gas limit, then we'll go ahead and
do I subscription ID equals subscription ID will do
I gas
Lane equals gas lane will do I call back gas limit equals call
back. Yes limit. Okay, a lot of variables set up.
But those are the variables that we're going to need for the
chainlink VRF. Now we have all these variables down in our
request. And if t we can request a random number to get for our
random n of t, we're going to say and our returns a yuan
tivity six Request ID. So in here, we'll say request, Id this
request ID that we just initialized I underscore VRF.
Core didn't a Tor dot r
equest. random words. And this should
look pretty familiar to what we did in our lottery. I underscore
gas Lane comma, I underscore subscription ID, comma, request,
con for nations. I underscore callback gas limit. And then of
course num words and we can just literally copy paste this from
the documentation or from our last project, whatever you want
to do. So we are requesting this random NFT here. Now here's the
thing, though, we want whoever called this request function it
to be there and hav
e tea, right. And if we saw in our basic NFT,
when we minted the NFT, we call this safe mint which needed the
owner and the token counter. When we request a random number
four are NFT. It's going to happen in two transactions,
right? We're going to request and then later on we're going to
fulfill and it's going to be the chain link node that's calling
fulfill random words. So if it In the fulfill function, we just
do this safe meant message that sender, the owner of this NFT is
actually going to
be the chain link node that fulfilled our
random words. So we don't want that, what we want to do is we
want to create a mapping between request IDs. And whoever called
this so that when we call fulfill random words, which
returns with that exact same request, ID, we can say, Ah,
okay, your request ID X, you belong to the person who called
this request and of team, we're going to create a mapping
between people who call this and their request IDs so that when
we fulfill random words, we can pro
perly assign the dogs to
them. So up at the top, right underneath here, I'm going to
call them VRF helpers, we're going to create a mapping of un
256. To an address, we'll make this public which we should make
it private, but we'll just make it public s underscore Request
ID to sender. And then when we call this request, and F T, will
set the request ID to sender of Request ID equals to message dot
sender. Now, when the chain link node responds with fulfill
random words, what we can do is we can
say address dog owner or
the NFT dog owner is going to be equal to s Request ID to sender
of Request ID. This way, it's not going to be the chain link
nodes that are going to own the dog, but it's gonna be whoever
actually called requests. NFT. Okay, cool. So we have a way to
request a random number for our random NF t. Now, let's go ahead
and mint this random dog, this random NF T for this for this
user. So we have the user now using this mapping, what else do
we need? Well, we're gonna need t
he token counter here. Let's go
ahead and we'll create a token counter variable. So we'll
scroll up and make a new section. And we'll say, un 256.
And then again, we'll just make a lot of these public just to
make it easier, but you might want to make this private and
use that same syntax we were doing before do s underscore
since this is a stored variable, token counter. And we'll grab
this token counter. And we'll say you went to 56. New token ID
equals as token counter, now that we have the d
og owner, and
the token ID, we can go ahead and mint this MFT. So we'll do
safe mint. Owner, new token, ID and then
safe man is going to be squiggly because our code is gonna say,
what is this? What is the safe mint function? Where did you get
this from? Well, we're gonna need to get it from open
Zeppelin again. So we're going to go ahead and do import at
open Zeppelin slash contracts slash token slash ERC. 721 slash
ERC. 720 one.so We'll say a random IPFS empty is Vera of
consumer base, and als
o ERC 721 In our constructor, right after
our V RF consumer base, we're going to put the ERC 721. And
same thing, we need to give it a name and a symbol. So we'll call
this random IPFS NF T. Comma, we'll just do Rin for random
IPFS NF team. Now safe mint actually works and then it's
gonna be mad at me for this. So I'm gonna do override just let
it stop getting mad at me. public view override turns
string memory. Just that squiggly line goes way. Okay,
cool. So great. So now we can safe mint to t
he dog owner this
new token ID Are we done with this? Absolutely not. Why not?
Well, we don't know what this token looks like. And what we
set above is we want to actually make these dogs different
rarities. So how do we actually create these dogs with different
rarities, all we could do is we create a chance array an array
to show the different chances of these different dogs here. So
down below, we're going to create a function and it's going
to be a public pure function called get chance arra
y. And
this is going to return you went to 46 of size three in memory.
And this chance array is going to represent the different
chances of the different dogs. So we're gonna say return
10 3100 Or we're gonna say Max chance value. And up at the top under empty
variables. We're gonna say you went to 56 internal constant,
Max chance value equals 100. So by making this array, we're
saying index Zero has a 10 percent chance of happening. We're
saying index one has a 20 percent chance of happening be
cause it's
going to be 30 minus 10. And then we're saying index two is
going to have a 60 percent chance of happening, because it's going to
be 10 plus 30. minus this 100. This array that identified the
percentages of the different dogs. We're saying the pug is
going to have a 10 percent chance, Shiva in you a 20 percent chance and
the St. Bernard a 70 percent chance, we're going to use it to give
this token ID that we just minted its dog breed. So we're
gonna create a new function called Get br
eed from modded
RNG. And the reason we're calling a get breed from modded
RNG is exactly the same way. In our lottery. We got a random
number, we're gonna say you went to 56 modded RNG equals random
words of zero mod Max chance value, we're going to mod any
number we get by 100. Doing it like this, we're always going to
get a number between zero and 99. If random words zero mod Max
Chin's value is going to be seven. That means we're gonna
get a pug. If we get 88. That means we're gonna get a St.
Bernard, if we get a 45. We're gonna get a what? That's right,
a St. Bernard, if we got a 12 We're getting a Shiva in you. If
the mod ID number that we get by modeling this random word is
between zero and 10. It's going to be pug between 10 and 30.
shiba inu between 30 and 100 St. Bernard and that's how we get
these randomness values. So now that we have this modded RNG, we
have this mod ID number that's going to be between zero and 99.
Okay, this function called Get breed from modded RNG. And
this
is going to take the UN 256 mod ID RNG will make this a public
pure function, and it's going to return the breed of the dog. Now
the breed of the dog is going to be an email similar to raffle
state that we did before. We're going to do this right at the
top since this is going to be what a type declaration or say
enum. Read, we're going to say the zero with number is going to
the pug. The ones number is going to be the Shiva in you.
And then the second one is going to be the St. Bernard. So
we
have the pug which is zero to 10. Shiva in you 10 to 30 St
Bernard 30 to 100. So get breed from modded RNG public pure
returns breed. So we're gonna loop through this we're gonna
say you intuitive six cumulative sum equals zero, we'll say you
went to 56. size three memory chance array equals yet chance
array. So we're getting that chance array. So we're going to
create a little for loop we're gonna say for you went to 56 i
It's gonna start with zero is going to be less than the chance
array
dot length. i plus plus ran through that for loop here.
And we're going to say if modded RNG is greater than or equal to
this cumulative sum and modded RNG is less than the cumulative
sum plus chance array plus wherever we are on the chance
array, then return breed of AI. And then outside of this, we're
gonna say cume cumulative sum plus equals chance array of I,
let's say moderate orangey equals 25. And if it's 25, it
should be a Shiva in you, because that's between 10 and
30. So we're saying i
f mod RNG, which is 25 is greater than or
equal to cumulative sum, which right now is zero, and it's less
than n, the Moto G is less than the cumulative sum plus the
chance array of I which is going to be 10 Return breed of I
cumulative sum is currently zero plus chance of I which is 10 is
going to be 10. And this is not true because Morarji is 25. So
since this is not true, we're going to move to the second step
which is just cumulative sum, plus equals chance right? So
cumulative sum will now
be equal to 10. And then we reached the
end of the for loop. So we'll restart i is now one, so let's
try this again mod RNG is greater than or equal to
cumulative sum Okay, that is true cumulative sum is 10. Mod
RNG is 25. And mod RNG is less than cumulative sum plus chance
re of AI, which is 30. So we're saying 25 is less than 10 plus
30, which is 40. Return breed of AI, this is true. So breed of AI
would be true and AI at the moment is one. And if we scroll
up, that's indeed the Shiva in you.
So that's how this function
is going to work, it's going to get us the breed from that
modeling bit. And then if for some reason, some really wacky
stuff happens here, we want to just go ahead and add a revert,
right, because we should be returning a breed. But if we
don't return a breed, we should just revert. So we're going to
create a new error at the top, random IPFS NFT underscore
underscore range out of bounds. The down below, we're just going
to say if for some reason you don't return any
thing, just do a
revert random IPFS NFT range out of bounds. And now we have this
function. Okay, so now we can get the brief from a modded RNG.
So pack in our fulfill random words function. Let's go ahead,
and we'll say, we'll uncomment this will say breed, dog breed,
equals get breed for modded RNG pass the modded RNG here, and
let's move this safe mint down below us getting the dog breeder
so we can mint and add the dog breed at the same time. So we're
gonna go ahead and safe mint here. Now w
e can do a few things to
set this dog breed here, we create a mapping between the dog
breed and the token URI, and then have that reflected in this
token URI function. Or what we could do is we could just call a
function called Set token URI and the openzeppelin ERC 721 You
have to set this token your eye function yourself. However,
there is an extension in the open Zeppelin code called ERC.
721 Uri storage and this version of the ERC. 721 comes with a
function called Set token URI where we can
just call set token
URI. And this will automatically update that tokens token URI to
whatever you set it as. So we're going to use this extension this
set token URI in our contract. And the way that we do this is
instead of doing token ERC 721 Year C 721 that soul will do
token ERC 721 slash extensions slash your C 721 Your arise
storage. That's all. And we'll say random IPFS is ERC 721 your
rice storage. Now what's cool is that our constructor will still
just use ERC. 721 Because ERC 721 Uri st
orage is extending ERC
721. And then this contract just comes with some additional
functions like set token URI. So right after safe meant, we're
actually going to call set token URI with this new item ID. And
then we're going to give it that breeds token URI, we're gonna
give it a string here that relates to whatever breed that
we just got based off the dog breed here. Now, to do this,
what we could do is right at the top and our NFT variables, we
could create a string array, internal, Alt S do
g token, your
eyes, we're gonna make this constant where it would just be
this array of all these strings that we created. But maybe in
our code, we want to make this a little bit more variable. And we
want to parameterize this. And that's exactly what we're going
to do. So we're going to create this string array, internal s
underscore dog token, your eyes, which is just going to be a list
of these URLs are these URLs that point to stuff like this,
we're going to do that in our code so that when
we upload any
image that we want to IPFS we can then upload this s dog token
you arise accordingly. In our constructor, we're actually
going to take in another parameter called a a string of
size three, memory dog token you our eyes, we're going to pass as
a constructor parameter, these different dog token you arise.
So we're going to pass it this list of dog token arise. Of
course zero is going to be the token URI of the pug. One is
going to be the Shiva in you and two, of course is going to b
e
the St. Bernard. So we're going to pass it this list of dog
token your eyes, and then down in set token your eye from that,
that list that we created. We're going to set the token URI of
this token based off of that array of the UN 256 version of
that breed are casting this dog breed back into you and 256 to
get its index. With that we now have a way to actually,
programmatically get a provably random NFT with different
randomness for different one of these NF T's. Now, let's go back
up to our
little, our little comments we made here, limited
NFT, we trigger a chain link or a VRF. To call random number, we
got the rarities down, we got the minting down. Awesome. Okay,
we don't have this part, though. Users have to pay to Minton NF T
and the owner of the contract can withdraw the eath. Okay,
this is stuff we've already done before, this should be pretty
familiar here. So back in our request NF t function, we'll
make this a public payable. And all we'll need to do is we'll
just say if
message dot value is less than some mint fee, and
actually let's go back to our constructor, we'll create a mint
fee, you int 256 Mint fee. And then we'll do you in 256
internal I underscore mint fee, we'll make this immutable. And
then we'll just say I'm intervene equals mint fee. If
message of value is less than mint fee, you already know we're
going to do a revert, need more eath sent, we'll create a new
air called need more eath sent and actually we'll do air random
IPFS and ft underscore un
derscore need more II sent
like that, copy this, paste it here and boom. So now just by
adding this line, this is now a payable function. And people
have to pay some mint fee to mint their randomized NF t. Now
we're also going to want a way for owner to withdraw. So we'll
scroll down a little bit. We'll scroll down to here, CREATE
FUNCTION withdraw, this will be public. And we only want the
owner to do this. So we could create our modifier, you know, a
modifier again only owner, or what we could
do is use
openzeppelin. Again, openzeppelin also comes with
some access code, or one of them's this ownable code, and in
here, it already has the only owner modifier for us, we're
just gonna go ahead and import that as well. We'll do import at
openzeppelin slash contracts slash access slash ownable dot
som, and we'll say contract random IPFS. And if t is this,
this come ownable. And then we'll make our withdrawal
function only owner. And so whoever deployed this contract
is going to be set to b
eing the owner, which is what we want.
And when here, same as what we've done, we'll do u and 256.
amount equals address this dot balance. And then we'll do bull
success comma equals payable, message dot sender, but call
value is going to be a mount and then we're going to call
nothing. And then we're going to say if not success. And we're
going to revert with transfer failed, then on top, we're going
to do error transfer failed. I'm just going to copy this a little
quicker, like so and then com
e back down to transfer failed
paste it here. So we're gonna revert random IPFS. And if T
transfer failed, perfect, so now we have a withdrawal function
and a way for people to pay for art here. Now we don't need this
token URI anymore, because when we call set token URI, this is
going to set the token URI for us because in the back ERC 721
Uri storage already has that function laid out. So our
contract will already have the token URI function and we don't
have to explicitly set it ourselves. Bu
t we do have to
explicitly set some other ones. We are going to need function
get meant fee will be a public view turns you went to 36 turn I
meant fee will need function get dog token your eyes, you went to
36 index, this will be a public view which will returns the
string memory return s underscore dog token your eyes
index will need function get token counter. This will be a
public view turns you into 56 Return s underscore token
counter. Alright, so we just wrote a ton of code here. And of
c
ourse as we taught you before, we also are going to need some
events. So when we request an NF T, we're going to emit an event.
So we'll omit NF T requested and then we'll pass it the request.
ID in the message dot sender. So up at the top will say event NF
T requested. So pet, take a un 256 index Request ID, and then
an address requester. And then we're also going to make an
event for NF T minted for when it's finally minted. And it's
going to take a breed, dog breed, and an address, mentor.
So
write down when we fulfill, we're going to emit NFT. minted. And it's going to take that dog
breed and then the dog owner. Okay, we've just written a lot
of code here. So let's go ahead and see if we can compile this
with H H compile, or yarn Hardhead, compile. And Wow,
looks like we went ahead and compiled it. This is great. So
all of our code now looks good. Now might be a good time to take
a quick breather, we've just written a lot of code. And it
might be good to just go over all the stuff
that we just went
through. A lot of this is familiar, but it's still really
good to redo some of this stuff and really get that muscle
memory and for these credit, an NFT contract that when you mint,
one of these NF T's, you're gonna get a pug a Shiva in you
or St. Bernard, based off of some rarity, where the pug is
really rare. Ship it in you is sort of rare in the St. Bernard
is pretty common. The way we do it is we have this request NF t
function, which people have to pay to call and it makes
a
request to a chain link node to get a random number. Once our
contract gets that random number uses a chance array to figure
out which one of the NF T's we're going to actually use for
this for that minting. And we're going to set the token URI
accordingly. And we're going to store the image data for this on
IPFS, which we haven't done yet. So our deploy function for this
is going to be really the interesting part of this
contract. But because we just went over so much, if you want
to take a
quick break quick breather, and then come back, I
encourage you to do so we just learned a lot. And we wrote a
lot of solidity code. So go take a quick breather, and I'll see
you in a minute. So let's go ahead and
get on in here, create a new deploy, go to ploy, random
ipfs.js. And now this is going to look really similar once
again, to the lottery contract that we've already done. And we
can copy some boilerplate from our code over here, we're gonna
need all this, we'll just copy that, close it
off with a little
curly and boom, we've already got a boilerplate. Now, since
we're working with chainlink, here, we are going to be working
with mocks again. So we're gonna come back new file 00, deploy
mocks dot j s. And if you want, you can just copy paste from the
earlier section that we did with the with the raffle slash
lottery. Since we're going to be doing the exact same thing here,
I'm gonna go ahead and pause and you can copy paste from your
previous projects. Or if you want, once aga
in, you can just
come to the GitHub repo associated with this course,
come over to the deploy. And go ahead and grab the deploy mocks
right from here. If you grab from the GitHub repo, we also
are going to be working with a mock v3 aggregator, but I'm not
going to add that part in quite yet, because we don't need it
quite yet. So go ahead and pause the video right now. Copy and
paste the VRF coordinator mock or pause the video and try to
write the mock code yourself. Okay, great. So once you've
done
that, we're of course gonna need to concentrate ID network dot
config dot chain ID, because we're gonna need to decide if
we're actually on a development chain. So same as what we did
before then we're gonna say if development chains that includes
network dot name, then we're gonna say const VRF coordinator,
B to mock goes await ethers dot get contract VRF. Core. The name
for the to mock similar to the raffle we're gonna say let VRF
coordinate Tor v two address, same, the VRF coordinator v
two
address equals VRF. coordinator v two mach mock that address and
then we're going to want to create a subscription exactly
the same as what we did with our lottery. So we're gonna say
const, TX equals weight VRF coordinate tore V to mock dot
create subscription. And then we'll do const TX receipt equals
weight, TX dot weight one. And we're gonna get the sub ID from
this exactly the same way we did it in the lottery section. So
we'll say that we need that sub ID by saying sub subscription ID
will say subscription ID equals TX receipt, dot events, zero.
That args got somebody. So that's what we do if we're on a
development chain, else said the VRF coordinate tore V to address
equals network config of the chain ID dot VRF coordinate or B
to that then the subscription ID equals network config. Chain ID
dot subscription ID. Perfect. And then we'll just double check
our helper hard hat config. So that Rigby has both the VRF
coordinator v2. And we're also going to need a subscription ID.
So right now, I have our subscription ID from our past
project from our lottery project. But we can go ahead to
V or F dot chain link, we can go to V RF, that chain link can
make sure we're on rink B here. Let's see our other
subscriptions. And it looks like we have one here. So I'm going
to copy this and paste it in for Rigby. Now again, we can go over
to DockStar, chain link EVM chains, contract addresses for
using reminisce to see more parameters in here, especially
for Rigby and make sure th
ese are all correct. We have our
subscription here, we'll add a new consumer very soon once we
deploy this contract if we're actually going to use RankBrain.
So we'll do a little log here with a bunch of hyphens. We'll
now get args equals, we'll make our arguments here. And what do
we need, we need the coordinator subscription, gasoline callback,
address, dog, your dog token, your eyes and admin fee. So
we're going to need VRF coordinator for the to address
subscription ID. And we'll need the ne
twork config. Chain ID dot
gas lane. We need the network config chain ID dot meant fee,
then we'll leave that work config chain ID dot callback gas
limit the right order? Yes it is. And
then we need the dog token your eyes and the mint fee with the
done here. Now what do we not have, we
don't have this array of token your eyes. Now we can do this
one of a couple of ways. If you go to the GitHub repo associated
with this, go to deploy, we actually did the randomness for
oh three in the get here,
there's one section where we
just automatically say, okay, great token, you rise is just
going to be these three. And then if we can actually even
copy paste these CDs on IPFS, these are the better ones that
actually do have the IPFS as the image. And we can see them like
that. So we could just use the stuff that I've already
deployed. And if you want to do that, you absolutely 100 percent Can,
or what we could do is we get to actually learn to upload
programmatically our own images to IPFS. Tha
t sounds a lot
cooler. So let's go ahead and do that. Now what I want you to do,
if you want to use your own images for this, feel free to do
so. But if you want to just follow along with us, then we're
going to go to the Hardhead NFT FCC repo. And we're going to
grab these random NF T's from him. So all you can do is you
can come to here. And we can go ahead, we can right click Save
Image, Jas, we'll save it to our downloads, let's actually create
a new folder, we'll call it images. And the ima
ges will
create a new folder called random, then we can pull it just
right into here, make the name random NFT. And this is just
going to be plugged up png. So we can do that for all of our
images. Now in our images tag, we have the pug ship in you and
St Bernard. So we have these locally. But we want to upload
these to IPFS. We want to upload them in a way that anybody can
actually pin them and work with them. So before we do all this
stuff, where we get the arguments to deploy this
contract, w
e're going to need to get the IPFS hashes of our
images. And there's a couple of ways we can do this. We do with
our own IPFS node, which I've already shown you how to do that
manually, we can also do that programmatically. Now, I'm not
going to show you how to do this here. However, if you go through
the IPFS documentation, you actually can learn how to do
through the command line and even through some scripts.
However, if we're the only node that's running this, again, it's
kind of centralized
. So ideally, we'd want these images and these
token your eyes and this and this token metadata on our own
IPFS node and some other nodes. So the second way that we can
look at this is using something like Hinata Hinata is a service
that basically you just pay to help pin NF T for you. And this
is going to be the one that we're going to be looking at
here. Now the issue with binotto of course, is that we're just
paying once single centralized entity to go ahead and pin our
data, we're kind of tr
usting that they're actually going to
pin it and that they're not going to go down. The final way
that we could look into actually getting our data is was this
thing called NF T dot storage. And if T dot storage uses the
file coin network on the back end to pin our data now filecoin
is a blockchain dedicated to pinning IPFS data and storing
decentralized data. For us, the process is a little bit more
complicated. But NFT dot storage makes it really, really easy.
Now, we're not going to go over u
sing NFT dot storage in this
video. However, if you want to look into NF T dot storage, for
putting your data in the GitHub repo associated with this
course, we do have a script that uploads your code to NFT to
storage called in the utils folder, upload to NFT. Storage.
And if you want to go and try it out, I recommend that you do so
working with NF T dot storage will be one of the most
persistent ways to keep our data up. But it's still good to
upload your own data to your own IPFS node, which
we've learned
how to do manually and at least get one other person also
pinning your data. And then ideally, an entire decentralized
network, pinning your data, which is what NFS storage helps
you do. But for now, for us, we're just going to work with
pinata to keep it nice and simple for this video. And then
uploading uploading our metadata and our token, your eyes up to
IPFS will give us this list of token your eyes for our three
dogs. So up at the top here, we're going to do a little if
we're
gonna say if process dot e NV dot upload to pinata equals
true, yes, we're going to use the string true like that. Then
we're going to upload to pinata. Above here we're going to say
let's token your eyes. And we're gonna say token your eyes equals
await, handle token your eyes. And
we're going to create a function called handle token your eyes,
which is going to upload our code to pinata down outside of
this, we're going to create a new function called async.
function. And we'll token your eye
s. And this is going to
return an array of token your eyes for us to upload to our
smart contract, we're gonna say token, your eyes equals this and
then way at the bottom, we're gonna say return token your
rise, right, so we're going to be returning this array here.
Now we need to do two things, we need to both store the image in
IPFS. And then we need to store the metadata in IPFS. So first,
we're going to create a store images function. And this is
where we're going to actually get to go to ou
r utils. And
we're going to create a new folder in here, we're going to
create a file called New File, Upload to pinata dot j s, we're
going to add all of our code for actually uploading to pinata in
here. Because again, pinata is this service that we're going to
be using to just pin data for us and work with pinata, we can go
ahead, try for free. And we can create our own application. And we're good to go. See the
setup here looks really similar to an IPFS node, because that's
essentially what
pinata is. It's just an IPFS node, run by
somebody else. And we can say, Hey, can you please pin this
data for us. So a manual way we could do this is we could just
hit Upload Cid just like an IPFS node and put the hash of some
IPFS file, and pinata would pin it for us, we could also upload
a file or a folder just like an IPFS node. But for us, we're
just going to leave this blank because we're going to want to
do this programmatically, because we're engineers. So what
we can do is we come over
to our profile, we'll open up API keys
and documentation. And the documentation pretty much has
everything that we need to get started. If you scroll down to
the pinata Node js SDK, this is basically what we're going to be
working with, they've already created an SDK for us that we
can work with, we're gonna go ahead and install this pinata
SDK. So they're using NPM, install dash to save, we're just
gonna go ahead and use yarn, add dash dash dev at pinata dash
SDK, and they have all these differ
ent endpoints, we can call
to actually pin data, we're going to be doing pin file to
IPFS, because we're going to upload our files and also pin
JSON to IPFS. Since JSON is going to be the metadata, and
file is going to be the actual image. And if you click on it,
it'll even give you kind of the output of the SDK here. So back
in our code, now that we've downloaded this, we can go ahead
and start creating this. So we'll say const pinata SDK
equals require at pinata slash SDK, and then we'll creat
e a
function async function called store images, it'll take an
images file path. So we're going to use this function will pass
it our images, random n of t file paths, and we're going to
have it store everything in that folder to help us work with
paths. We're also going to Install this path package. So
we're gonna do yarn, add dash dash dev path, like, so, we're
just going to work with Fs as well not Fs extra. So now that
that's up, we're gonna say const. Path equals require path.
And in here,
we're gonna say const, full images, path, equals
path dot resolve, images file path. So if we give that like
dot slash images, slash random, you know, NF T or whatever, this
will just give us give you the full output of the path. So
we're getting the full images path, and then we'll say, we'll
get those files by doing const files equals Fs. So we'll do
we'll grab s will say const Fs equals require Fs just to read
these files in here, that read DirSync. We're just going to
read the entire directo
ry and get our files back to read
DirSync full images path. And to actually test that this is
working, what we're going to do is we're going to do module dot
exports, equals store images. And then back in our deploy
script here, we can just go ahead and comment out args. And we can do
import const, store images equals require dot dot slash
utils, slash upload to pinata. And in this script, we could
just do a little, we just call this we could say await, or
images, and then we'll pass pass our im
ages location, maybe way
at the top, even outside of the function, we'll say const images
location equals dot slash images slash random and ft. So we'll do
a weight store images like that. And we should be able to run
Hardhead deploy. Then if we add some tags to this deploy thing,
module, dot exports, dot tags equals, we'll say all random
IPFS. And then main, we do Hardhead deployed dash dash
tags, random IPFS will also do mocks believe, and I need to
create a test folder in here New Folder test
. And we need to add
that VF coordinator v2 Mock in here. So again, I'm just copy
pasting the VRF coordinator v2 Mock from our raffle project.
Feel free to pause, copy, paste it over, or again, everything's
available on the GitHub. So we'll try one more time. Tags,
random IPFS, and mocks and boom, okay, mocks deployed and perfect
pug dot png ship any png St. Bernard PNG. Great. So we're
getting the files correctly here. Now let's create a little
array for responses from the pinata server. So we'
ll say
responses equals this. And we'll say for each file index in files
for each one of these files in here, we're gonna say const
readable stream for file equals Fs dot create read stream of the
full images path slash files of file index. What does this line
doing? Well, we're creating a read stream. Since this is an
image file, it doesn't work exactly the same as just like
push this data, right? We have to create a stream where we
stream all the data inside of these images. Because these
imag
es, even though they're just like a cute little image here,
they're really this kind of this big file with all this bytes and
all this data in here, and then we're going to send it by doing
try, we'll say const response equals await. And this is where
we're going to do pinata stuff. If we go back to the pinata
docks, there's some stuff about keys in here. What we can do, if
you go to your profile, go to API keys, we can create a new
key, we'll say this is an admin key, why not? We'll give it all
the pinning access here. Maybe we'll give it this, maybe we'll
just give it everything, whatever you want to do. And
then we'll call this hard hat, Free Code Camp key, create key.
Now we're going to want to grab these and drop these into a dot
env. So the API key, go ahead and copy, come back over here.
Open up our dot env. And we're gonna call it our pinata. And
data API key equals that key. We're going to grab the API
secret and say pinata API secret equals that key. We don't need
this massiv
e token here for what we're going to do. But if you
want it, you can absolutely have it. And then outside of our
store images, we're going to say const pinata API key equals
price says, do you need that pinata API key? And then can't
pinata. API secret equals process dot E and V dot pinata
API secret. And then we'll say const. pinata equals pin auta s.
DK of pinata API key comma, pinata API.
Secret in order to work with pinata, we need to pass it an
API key and API secret so that pinata knows it
's us who's
working with them. So once we initialize this pinata thing, we
can now run pinata dot and then do some pinata stuff, right, we
want to work with this pin file to IPFS, which takes this
readable stream, which is why we created that readable string, so
pinata that pin file to IPFS. Of readable stream or file. And
then we're going to push this response on to our responses
array. So we'll say responses dot push response. And then
we're going to catch error, just in case there's some weir
d error
here. And we'll just say console dot log error, and then we're
going to return responses and files. So we're going to return
all the responses from pushing all these files up, and then the
files as well. Now, at this current point, we can go ahead
and actually test this out ourselves. So we have this in
here. If we go back to our deploy, at the top, we have this
if process dot E and V dot upload to Niada equals true, do
this stuff here, uploading to IPFS. And the final thing we
need to d
o in here, of course, is going to be require dot env
dot config, so that we can pull in our dot env file. Down here,
we're just doing a weight store images. So if we run this as is,
it should go ahead and store images. So let's run that same
command, or that deploy random IPFS and the MOX to run this
store images command. And if we come back to our pinata, after
we run it, we'll be able to see the code uploaded here. If we
run now, the script loaded IPFS it'll give us a little bit of a
delay, be
cause it needs to upload these big picture files
to IPFS. or more correctly. pinata, I'm gonna say uploading
to nada. And we could even say, console dot log, working on file
index, done Excel. And okay, great. It looks like it finished
running. So if we come back to our project here, and we do a
little refresh, we see our three files have been uploaded, and we
see they each come with their own CID. Now, if you want, you
can go ahead and copy the CID. And if you have your IPFS node,
what you can
do, what we can do is actually we can hit import
from IPFS. Paste it in here. And we can say exactly what this is,
which is IPFS dot dot slash last as the St. Bernard called St.
Bernard. Important. And now we'll have it pinned on our
IPFS. I've got mine saved in a little puppies file. Now that
we've got them uploaded, and pinata do actually recommend you
pin your own on your own node as well. So cool. So we've got a
way to get those images up onto IPFS. Awesome onto. Now that we've done that,
we
're also going to need to store the token URI metadata. So
let's go ahead and we'll delete that for now. What we can do
back in here, is back up at the top again, we can say const meta
data, template equals and we'll create a metadata template. This
is going to have all the basics of what we need for our metadata
for our token URI. So in here, we'll have a name, set, it's
blank, we'll have a description, which will also set as blank
will have the image which this is going to be replaced with the
image URI that IPFS URI we just created. And if you want to give
your NFT like any types of stats, you can do some you can
create this Attributes section like so. And if you give it like
traits type, cuteness, comma, value 100. And this is how if
you wanted to create like different cards or have
different attack, defense, HP speed in different different
stats for your NF T's, you would add them in this Attributes
section. Typically, you'd want these attributes also stored on
chain so your contr
acts can obviously programmatically
interact with these attributes. But so now we have this metadata
data template. This is what we're going to fill out for each
one of our dogs. Yes, we're going to create a new function
in here called async, store token URI metadata. And we'll
pass in the meta data to the async function. And we'll pass
in the metadata that we get from our script over here. So we have
this little template here. And we're going to populate this
template based off of what we get f
rom storing data in IPFS.
So now we're going to write the rest of this handle token your
eyes bit. And so we're going to want to do in our data and V,
we're going to say, upload to pinata equals true, so that we
can do everything and handle token your eyes. So upload to
pinata, it's true, we'll scroll down, we'll start creating this.
So the first thing we got to do, obviously, we're gonna want to
get those responses and those files, right, because it's in
the responses, pin file to IPFS is going
to return the hash of
the file, right, and we need that hash, to add to our
metadata. So we're going to do is down here, we're gonna say
const, responses, which is going to be image upload, responses,
comma files, equals await, store images, and then images,
location. And so this response is, is going to be a list of
these responses from pinata. And these responses are going to
have the hash of each one of these uploaded files. So now
we're going to loop through that list and upload each of the
metadata does. So we're going to say for each image upload
response index, in image upload responses. For each one of these, we're
going to create the metadata, we're going to create metadata
and then upload the metadata. So we're gonna say let token URI,
metadata equals dot dot, dot meta data template. So this is
some fun JavaScript, syntactic sugar, which kind of means like,
unpack. So basically, we're saying token URI metadata is
going to be equal to this stuff. We're sticking all this stuff
into this token, your metadata variable, now we're gonna say
token, your metadata. dot name is going to be equal to files
of, of the index, dot replace, dot png, dot png with nothing.
So files is going to be each one of those files, right? It's
going to be dot png can be St Bernard dot PNG, and it's going
to be Shiva dot png. And basically, all we're doing is
we're saying, okay, cool, the name inside of our token,
metadata is just going to be pug. So we're just going to drop
the extension, basi
cally. So that's how we're gonna get the
name token, your AI metadata dot description, is going to be
equal to and adore a bowl. And then we're just going to get the
name, token URI metadata dot name. So it's going to be an
adorable pug pup and adorable St. Bernard pop or an adorable
ship and you pup token URI metadata dot image, which is
probably the most important one here, this is going to be, it's
going to be that IPFS extension with the IPFS hash that we get
from the response. So we can get
that by doing image, upload
responses of the image upload response index dot i IPFS. Hash,
so we can go to the pinata docs. And we can see pin file to IPFS
returns in IPFS hash, the pin size and the timestamp, all we
care about is the IPFS hash. And we're going to use that to give
the our metadata image here. And then finally, I'll do a little
console dot log uploading. And then we'll say, token, your eye
metadata dot name, dot dot dot. And now we'll have to store the
file or store store the JS
ON to pinata slash IPFS. And this is
where in our upload to pinata bit here, we're going to add
this function here. So we have store token URI metadata. And
all we're gonna do in here, so we're gonna say try const
response equals await, and nada, that pin JSON to IPFS of the
metadata, right. And again, we have, we want pin JSON to IPFS,
which is going to be really similar. So we need to pass the
body, which is going to be the JSON. And we have some optional
stuff here, but it's gonna give us the
same return the IPFS hash
pin, size and timestamp. And then if this works, well, we're
just going to do return response. Otherwise, we'll do
catch error. And then we'll just do console dot log error, and
then we'll just do return null and then we'll exports or token
URI metadata. And then back in our deploy, we'll go ahead and
import this store token URI metadata. And we'll scroll down.
And they'll do const, metadata, upload response equals await,
store token URI metadata where we pass the toke
n, Uri,
metadata. And now finally, and now with all of these metadata
is being uploaded are finally going to have the token you
arise that we need. So we'll say, token, your eyes dot push,
IPFS, slash slash, and then the metadata response here, metadata
upload response dot IPFS. Hash. So we finally will have this
array of IPFS hashes that points to the metadata, and each one of
these metadata are pointing to the image. And then we'll do a
little console dot log token. You arise, uploaded, they a
re
and then we'll do another little console dot log token your eyes.
Oh, all right. So let's go ahead and run this. And let's see if
it works. In our pinata, we should see both the images and
then also the metadata. If process dot E and V dot upload
to pinata equals true, looks like it is true. And we should
run this and handle token, your eyes will both upload our images
with store images here and then upload our metadata. We'll only
see it in here once because again, it's gonna have the exact
same Cid right, it's gonna have the same hash, so we won't get
duplicates of the same file in pinata, or in our IPFS. So let's
open this up. Let's run this one more time, or had deployed dash
dash tags, random IPFS and mocks. Okay, it looks like we
almost worked uploading to banana working on zero working
on one working on two metadata template is not defined. Ha,
because I spelt meta data template, meta data template,
let's spell things correctly. Now let's try this again, it
working on zero is
it working on two image upload response is not
defined. I should spell correctly. There we go. Let's,
let's do our caps correctly. Let's try this one more time.
Okay, 012, uploading pug uploading Shiva in you uploading
St. Bernard token your eyes upload it and they're here. Now
if we grab this, and stick it into our browser, or your IPFS
node, boom, we have them in here. And what we can do is we
can grab this hash, we can jump into our IPFS desktop, go to
files, we import from IPFS paste it in
here. And you know give it
the name etc, that we have it on our own IPFS node as well.
Awesome. Now if we go over to pinata give this a little
refresh, we can see we have everything in here. So if I copy
one of these, and I go to IPFS dot dot slash paste that in, we
can see the metadata in here. And everything looks good. So
this is absolutely massive that we've just done this because now
we can store data both on our own IPFS node and at least one
other nodes so that if our computer goes down o
r our server
goes down, and there's at least somebody else who's done it. Now we finally have this list of
token your eyes, we can finally go back to our arguments. And
now we can do like this. And we can upload all of those token
your eyes to our smart contract. And then we're going to need a
mint fee as well for this. So if we go to our upper heart at
config will create like a little mint fee in here, say mint fee
is going to be whatever we want 12345678123456789 What we'll do
is 0.01 eath. So
we'll even drop this down one, we'll do it for
we'll do it for the Hard Hat Network. And then we'll also do
it for Rinckey as well. We have the arguments here. Now we can
finally deploy our contract. So we'll do const random IPFS. And
if t equals await, deploy, if the contract is random IPFS NF T
from Deployer. args is args. Log is true. And then wait
confirmations is going to be network dot config dot block for
confirm terminations, or one, and great they might do another
little line here. We'
ll do the verification bit. I'm actually
just going to copy that from our deploy. Our basic I'm just going
to copy this it's going to be code is going to be exactly the
same, except for we're going to verify random IPFS instead of
and then actually we use args instead of arguments, then that
is it. So we'll give this a quick deploy test, HH deploy or
yarn Hardhead deploy. And it looks like we almost worked we
almost got Everything's done. Network config is not defined,
because we didn't import i
t. So let's go ahead and import
network config from our helper Hardhead. Config. So import
network config. We'll try this one more time chain ID is not
defined, it's going to be chain ID, that chain Id try one more
time. And awesome, it's at least working for our heart Hat
Network. And then we can copy this array, and just paste it in
here. Boom. And now we have this token your eyes. And now back in
our dot env. Now we can set this to false. Since we have all
those token neurons already, we just
run hard hit Deploy, we'll
just use the token heroes that we already have uploaded. Yes,
we have done it. Now I know you're excited to see this on
something like open See, you're excited to see this right away.
But let's save deploying all these to rink B for our last
thing, because again, deploying to test nets is really slow. So
let's just wait until our last bit and then we'll go ahead, and
we'll deploy this to rank B. But before we write tests, something
that I noticed when I was writing my
test is that we
forgot to update the token counter. So in here, before we
do, our safe, men will do s token counter plus equals s
token counter, which is going to be esto. Encounter equals es
token counter plus one. And that's a perfect example of why
writing tests is so important. Before you write any tests,
you're definitely going to need to fund that subscription, which
we didn't do in here. So we need to do a wait VRF coordinate Tor
V to mock dot fund, sub scription. subscription ID,
comma
will do fund amount, and then just up at the top,
actually, we can even pull this out too. Since that's not gonna
change at all. We can do let token year I was like that looks
like const fund amount equals cool. Or you could do you know,
you could also do ethers dot parse. But as you know, before
we can even deploy to rink beam, what should we do? Well, you got
it right, we should absolutely 100 percent write some tests. So we're
create a new file called random IPFS NF t dot test dot j s. And
we
can write some tests here. Now, once again, there isn't
anything new that you're going to learn in this test here, it's
going to be very similar to the lottery tests that we've written
before. So here's what I'm going to say, I'm going to highly
recommend once again, that you try to write at least two or
three of your own tests, but definitely write a test fulfill
random words. But I'm going to encourage you to pause the video
now, please pause the video and try to write some tests yourself
str
uggling with some of these tests. And writing some of these
tests is going to be what really gives you those coding muscles,
if you will, that's going to give you the skills to keep
writing these tests and be really fast and really
efficient. When you're building these smart contracts. These
tests are the tests that protect you from writing bad immutable
code. So please pause the video, I'm going to copy paste from the
GitHub repo. But please take this time to write some of your
own tests here.
Alright, great. Did you write
some tests? Hope you did. If you didn't write some tests, pause
this video and go write some tests. I promise you doing these
tests, writing exercises on your own will help you dramatically
at this point, what have we done, we've done some some
amazing stuff, we've deployed a basic NFT with pretty much
nothing to it, then we've deployed a provably random NFT
with random stats with random traits with different rarities,
for each NF T's depending on when it was minted
, or who
minted it. We've stored the data for this on IPFS. And we've
learned how to programmatically upload our files to pinata,
which is another pinning service for us. We learned a little bit
about NFT debt storage, which is another way to pin data to IPFS.
And then of course, we learned that if we wanted to, we could
programmatically pin data to IPFS on our own node. But since
a lot of us aren't gonna be running our own computers 24/7.
We went ahead and said, Okay, well, we'll stick with pin
ata
for our default here. Now that we've done all that, boom, got
another little checkmark here. Now I got something to say we
don't need to host our data on IPFS can actually host our data,
our metadata directly on chain if we want to. However, there
are some pros and cons to IT pros of hosting on IPFS, or
that's going to be cheap. And the cons are that someone needs
to pin our data, right? There's at least one person always needs
to have her data pinned, right? It's decentralized, but you at
l
east need somebody to pin your data, right? And using something
like file coin is a way to incentivize people to pin that
data. But if you're not using File coin, it's not necessarily
guaranteed the pros of doing our SVG on chain NFT the data A is
on chain. And you never have to worry about somebody actually
pinning the data. The cons are that this is much more
expensive. These little images, right here are actually
surprisingly large and storing them on chain can actually get
pretty expensive.
So we're going to use some different images, so
much smaller images, much cheaper images to work with
here. And if you want to see another version of this, I have
another video, how to make NFT art with on chain metadata, it
goes through pretty much what we're about to go through here
as well if you want a second reference, and there's a link to
this in the GitHub repo associated with this course. So
instead of these PNGs that we're using, we're going to use
something called SVG s. Now, an SVG s
tands for Scalable Vector
Graphics. And these are much much more minimalistic files
that we can go ahead and upload to to the blockchain. So that's
why we're going to use them because since there's so much
more minimalistic, they're a lot cheaper to upload. Because
remember, the more data that you upload to the blockchain, the
more expensive it is. Now in this video, I make randomize SVG
data on chain. And here's kind of an example of what one looks
like. It's just a whole bunch of random lines.
Not super
thrilling, but random and kind of cool. And it's 100 percent. On
chain, these SVG is actually work right in HTML. So if you
want to use these for your websites, you can as well now
there's a link to this tutorial in the GitHub repo associated
with this course, where we can go ahead and try it yourself.
And we can actually play with making an SVG, right, so they
have all these different commands in this web through
schools.com/graphics/svg intro dot ASP, you can see some of the
differe
nt commands, right, you can make a rectangle you can
make a circle path is a big one where you can say exactly what
the path or the line you want to draw is going to look like.
There's a whole bunch of stuff you can make in this SVG. And
the cool thing is, no matter how big you make an SVG, the quality
is always going to be exactly the same, because SVG just
explains exactly how to draw it no matter how big or how little,
the image is going to be. So if you want to learn more about SVG
is you wa
nt to play with SVG is you know, you can come in here
and try it yourself. So with that being said, that's what
we're going to store on chain. So that's how we're going to
store this SVG stuff on chain. But we're gonna go one step
further, we're gonna make this dynamic, we're gonna make this
actually change based off of some data on chain. If you go to
the GitHub repo associated with this lesson, and you go to the
images, and you go to dynamic NFT, you'll see two images, see
happy dot SVG, which
looks like this. And you'll see frown dot
SVG, which looks like this. So we're going to make this NFT
dynamic in the sense that we're going to say, if the price of
eath is above some number, then we're going to have it be a
happy face. And then if it's below that, we're going to make
it a frowny face. So our NFT is going to change based off of
some real world parameters. And this is obviously really
powerful and really cool, because we can have an NFT that
changes based off stats, we can have a
n NF t that changes based
off of really whatever, and we're going to store all the
data 100 percent on chain, it's going to be a little bit more
expensive. So that's what we're going to be building here. Let's go ahead, let's jump into
it. And let's do the final contract for our ultimate NFT
section. So we're going to create a new contract in here,
new contract, and this is going to be our dynamic, SVG and F T
dot soul. And it's going to look real similar to what we've been
doing. slash slash dy
namic. SVG and ft dot saw, slash lat spdx,
license identifier, and my team pragma. solidity, Eric zero,
point 8.7. Contract, dynamic SVG, NF t. Now let's talk about
what the architecture of this is going to look like. It's gonna
look like pretty normal NF T, with a couple of caveats. We're
gonna give it a min function to mint, these NF T's are also
going to store our SVG information somewhere. And then
we're going to need to have some logic to say show x image or
show y image, right. And as we k
now, that's really just going
to be switching the token URI to say show X or show y. So let's
go into how we'd actually do this. So first, we know this is
going to be in ERC. 721. So we can go ahead and import that
from open Zeppelin. So we're going to say import at open
Zeppelin slash contracts, slash token slash ERC. 721 slash ERC.
721 That's all. Now we're not going to call that set token URI
function that we call before so we can just use the raw ERC 721
instead of an extension. So we'll say
our contract is ERC
721 and Now that we're making it an ERC 721. We can say construct
dorm like this, then we'll call the constructor of the ERC. 721,
which we're going to call this dynamic SVG, and f t DSN,
dynamic SVG and s, t and f t, like so then we're also going to
need a mint function. So let's just create that right now say
function, mint and f t or request NFT. And we'll be a
little bit looser here, we'll say the user doesn't need to pay
any money for this. So this will just be a public
function. And
we're just going to mint them and NFT. Same thing, we're just
going to call Safe mint message dot sender. And of course, we
need that token count term. So let's go ahead and in our top
will do you in 256, private s underscore token counter, we'll
do token counter here. And then after we meant we'll do tag
counter plus, there's gonna be equals token counter plus one.
And then that's pretty much it, we have a way to meant we've
done some of the basics here. We'll even we'll be expli
cit
will say s token counter equals zero to initialize it. But now
what is this token going to look like? We want these to look like
SVGs. And we want it to be based off the price of some asset in
our constructor, all right, create a string memory. We'll
call it low SVG and a string memory, high SVG. And in our
code will save this low SVG in this highest V G. So these will
be the images, these will be like the frowny face and the
smiley face that will just import as input parameters here.
So as
we know, we can make these immutable since these are
probably not going to change. We can say String, private, I
underscore low image URI. And in string private, I underscore
high image URI. But if we just pass the SVG data, right, the
SVG data is going to look like what in this GitHub, I can go to
display the source blob here and I can see exactly what this code
looks like this code here is definitely not an image URI.
What we need is the image URI to look something like this, right?
Now, the w
ay that we're going to pass it in is like with this SVG
code, right? Because we want to just pass it the SVG code, and
then have the contract handle everything else. So how do we actually do this?
Well, what we can do is we can create a function called SVG to
image URI. And on chain, we can convert these SVGs from SVGs. To
image arise. So instead of having IPFS, as their start,
we're gonna use something called base 64 encoding, you can
actually encode any SVG to a base 64 image URL. That's right
.
It'll look something like this base. 64 is group a binary to
text encoding schemes that represents binary data. Or in
our case, our SVG data. Base. 64 is particularly prevalent in the
world wide web, or one of its uses is the ability to embed
image files or other binary assets inside textual assets,
such as HTML, and CSS, what we can do, can actually convert all
this SVG stuff to a URL or an image URI would be great, right?
That's exactly what we want, we want to be able to convert this
to a U
RL or an image URI. Now, if you take one of these images,
or these SVG images, like the happy to have SVG, what we can
actually do in this happy dot SVG is we can actually copy the
image address, which is going to be the URL of this address. And
if we paste it back in, we'll see justice file here. And in
this site, we can actually do data type, remote URL, paste it
in here. And we can say encode SVG to base 64. And down here,
we'll get this weird job role of numbers and letters and stuff.
This b
ase 64 encoding represents the SVG that we just got and
what we can do in our browser, we can type data, colon, image,
slash SVG, plus XML, semicolon, base, 64, comma, and then paste
that massive thing in here and Enter. And wouldn't you know it,
we get exactly that image back up. So that huge, massive thing
here is the base 64 encoding of this image. So with this basic C
four encoded image, we can use this on chain as the image you
arrive for our images. And then for our metadata. We'll just
ba
ke that directly into our token URI. You'll see what I
mean in a second. So we have a way where we can actually
directly put our SVG code right into our smart contracts. Let's
figure out how to do that. So we have function, SVG to image URI,
so we know we're going to want to probably do that same base 64
encoding on chain. We're gonna 100 percent do this off chain if you
want to save some gas but it's kind of fun for To show how to
do this all on chain, so we'll make this a public pure
function.
And we'll have it returns a string memory. So
we're going to give this function an SVG, which we're
going to pass in from our constructor. And we're going to
return a string, which is going to be that base 64 encoded URL
that we just saw, while up at our top will do string, private
constant, base S feed, base 64, encoded SVG prefix equals that
right there. And we'll use this to generate our SVG now, well,
we can do. And then we're going to do string memory SVG, we're
going to encode this SVG in
basically for ourself by adding
the base 64 encoding on chain. Now, we don't really want to
have to rewrite that ourselves. So luckily for us, somebody has
already done this. And we can see the GitHub repo associated
with this, this was created by one of the LoopRing devs really
awesome project, if you want to check that out as well. And
we're going to borrow this code for our SVG on chain. So what we
can do is we can add this in here, add their GitHub code,
where they have basically everything
that we need in here
to encode and decode basics before, we can do yarn, add dash
dash Dev, base 64 Dash soul, this is going to add their code
as a dependency. And once we've added it, we can go ahead and
import it with import, base 64, dash soul slash, base 64 dot
Sol. And this contract comes with an encoder. So then we can
just do string memory, SVG base, 64, encoded, equals base 64 dot
ENCODE. And here's where it gets a little bit weird. We'll do bytes string, abi dot
ENCODE, act, SVG, and t
hen we'll return string API dot encode
packed, base 64, encoded SVG prefix, comma SVG base 64
encoded, and just this function, this SVG to image URI will take
in any SVG and spit us back out a URL or URI that looks exactly
like this. Now, I kind of sped through some stuff in here.
There's a whole bunch of new stuff like ABI dot encode packed
that we did twice, what is this ABI dot encode packed doing? Well, let's learn about that. So
from a really, really high level, this is basically how you
co
ncatenate strings. Right, this is how you combine strings
together. And we're going to jump over remix to actually
explore this ABI dot encode pact and this ABI encoding stuff a
little bit more. Now the section that we're about to go through
is definitely advanced. And we're going to be going over
some really low level stuff, and how solidity works behind the
scenes, how the binary works, and this thing called opcodes.
And all this crazy, low level, tricky, difficult things to
understand. If you
want to move past this section, there are
timestamps in the GitHub repo. To help you move past this.
However, I do encourage you to at least try to absorb most of
this material, if you don't understand it the first time.
That's 100 percent. Okay, this is more advanced. Anyways, for most of
your basic projects, you won't really need this information.
It's only later on once you get more advanced that knowing all
this is really going to make you a phenomenal solidity developer.
And when you appro
ach this section, when you approach this
sub lesson on EVM, opcodes, and coding and calling, just know
that if you don't 100 percent understand it the first time
that is okay, if you want to watch this section a couple of
times fantastic. So if you want to jump over to remix and follow
along, let's do it. Now in our contract section, let's go ahead
and create a new file, we're going to call it encoding dot
soul. And remember, all the code that we're going to be going
with in here is going to be
in this sub Lesson folder of the
Hardhead NFT. FCC. And all the code we're going to be working
with is going to be in this encoding dot soul. And then in a
little bit, we're going to work on this call anything that soul
so we're in this encoding dot soul. And let's just make our
basic code here. So we'll say spdx, license identifier, MIT
pragma, solidity, carrot, zero, point 8.7, like that, do
contract encoding, boom, compile, or Command S or ctrl S
great things are looking good. Now remember, t
he whole purpose
for this is to first understand what's going on here, and more
about this API dot encode packed stuff. So let's first just write
a function that should owes us wrapping API dot encode packed
with some strings and wrapping it around a string is going to
return a string. So we could do function, bind strings, or
concatenate strings. This will be a public pure since we're not
going to be reading any storage, we'll say returns string memory.
And we'll say return string, API dot enco
de packed. I'm I'm
comma, space in here, I miss you, like so. We need another
parenthesis here. Okay, great. Now let's go ahead and deploy
this, we'll start a JavaScript VM will deploy encoding
coding.so. We'll come down here, we'll click Combine strings, and
we get that whole string output. Hi, Mom, miss you. So what we're
doing here is we're encoding hi mom misuse together into its
bytes form, because API dot encode packed returns a bytes
object, and we are typecasting it by wrapping it in thi
s string
thing to be a string. And solidity says, okay, yeah, bytes
to string, that's fine, that totally works. And this API dot
encode packed are these globally available methods and units. And
actually, in solidity, there's a whole bunch of these, there's
this solidity cheat sheet, and there's gonna be a link to this
in the GitHub repo as well, that has a whole bunch of operators
and it has a whole bunch of these global variables and
methods. You can see if we look in here, we look for ABI dot
encode pact, we see ABI dot encode pact right here, we
scroll down, we'll see some more that we're familiar with as
well. Like, for example, message dot sender, sender of the
message, message dot value. There's a whole bunch of other
globally available methods and variables that we can use when
we're coding our stuff. Now, I will say though, in 0.8, point,
well, plus, you can actually do string dot concat, you know,
string a, comma string B, if you want to, instead of doing this
API to encode p
acked, but I still wanted to show you the API
dot encode pack, because it's a great segue into all this ABI
stuff that we're about to go over. But let's focus on this
encode packed thing. So what is actually going on here? Well,
before we dive deeper into this encode pact, let's understand a
little bit more about what happens when we send a
transaction. So when we compile our code, and again, all these
pictures are going to be in the GitHub repo. Remember back to
ethers.js, we had those two file
s, we got a dot ABI file,
and a dot bin or dot binary. Back in our ether symbol
storage, when we ran yarn, compile. The two main files that
we got, were this symbol storage that abi, which was this, you
know, this ABI thing that we've become familiar with. And then
the simple storage dot bin, which is the binary, which has a
whole bunch of just numbers and letters and stuff we didn't
understand. And you can see that in remix to, like if we were to
compile this, you get a compilation details, you
get a
whole bunch of stuff in here, right? You can see the ABI in
here, which this is kind of like a different way of viewing that
API, we also get this bytecode bit and it's this object that
has the same stuff that has like those random numbers and
letters. But this is actually the binary this is actually
what's getting put on the blockchain. It's this binary,
it's this low level stuff. Now, when we actually send these
contracts to the blockchain, we're sending like I said, we're
sending this
binary thing. That's exactly what we're
sending to the blockchain. And remember how, again, back in our
ethers project, we saw what is a transaction, right, a
transaction has a nonce, it has a gas price guess limit to value
data, we kind of skipped over the VRS a little bit, because
that's kind of that mathy component of the transaction
signature. But again, back in our ethos project, we did this
as well, right in our deploy script, ended up sending a
transaction ourselves, using just ethers. We
passed a nonce,
a gas price, gas limit to value data was this massive thing to
declare a contract, and then also the chain ID. We didn't
work with the VAR s, because ethers does that for us. But
there's also this VAR s component that we don't bother
to look at. When we send a transaction that actually
creates a contract, the two is going to be empty. We're not
going to send this contract deployment to any address. But
the data of this is going to have the contract initialization
code and contra
ct bytecode. Right. So when we compile it, we
get all this code, like how do you initialize the contract, and
then what the contract actually looks like. So if you look at
any of the contracts that you deployed, for example, I'm going
to look at our raffle that we deployed. If you go to the
transactions of your contract, we can see create raffle, right,
let's go to that transaction. If we go down and click to see more
in the ether scan, we can see this input data thing. And once
again, it's got
all this random garbled numbers and letters.
This is that binary data of the contract initialization code and
the contract byte code right what we send in our trend In
this action is this data thing, we send this this weird bunch of
garbled nonsense. Now we're going to head back to remix. And
I'm just going to leave this as comments in here in the encoding
dot soul and the GitHub repo. There's a ton of comments in
here explaining exactly what I'm explaining. So if you want to
follow along there,
you can as well. But now in order for the
blockchain to understand, okay, what do these numbers and
letters even mean? You need a special reader cerium or the
blockchain needs to be able to read all this stuff, it needs to
be able to map all these random numbers and letters to what they
actually do. How does Aetherium or polygon or avalanche know
that all this nonsense is basically telling it to make a
contract, you kind of think of it as saying, like, take off
your coat. The only reason that w
e as human beings understand
what take off your coat means is that we understand English,
We're all reading English, for solidity. And for blockchains.
Instead of English, I read these numbers and letters kind of like
words, just instead of take off your coat, it's like deploy
contract, and the contract does next XYZ and all this random
stuff. So this bytecode represents the low level
computer instructions to make our contract happen. And all
these numbers and letters represent kind of an alphab
et,
just like how take off your coat is an alphabet. And when you
combine them like this, it makes something that to us makes
sense, you can kind of think of the alphabet for these as what's
called opcodes. If you go to create a new tab, if you go to
EVM dot codes, we'll get to this place where it just has a list
of all these instructions. On the left side, you can see this
thing called opcode. And then you can see name, so this opcode
section is saying, Hey, if you see a 00 in this bytecode, th
at
00 represents this opcode stop, which does what which halts
execution, if you see a 01 you're gonna do some addition
stuff, a 02 is multiply, there are all these op codes that are
kind of like the alphabet, or the language of this binary
stuff, right, and they go all the way down to f f self
destruct, these op codes also have, and that's what this is
reading. Right? So if we look at our transaction here, and your
yours might be a little bit different. Oh six, one says,
Okay, the first thing w
e want you to do is the O six, one
opcode. And if we go to EVM, opcodes, we look for six one,
it's saying push to place to buy item on the stack. That's
exactly how it's reading this any language that can compile
down to this opcode stuff down to this specific set of
Aetherium opcodes, or EVM. opcodes, is what's known as the
EVM, the Etherium virtual machine. So being able to read
these op codes is sometimes abstractly called the EVM, the
Etherium virtual machine, the EVM basically represents al
l the
instructions, a computer must be able to read for it to interact
with Aetherium, or Aetherium, like applications. And this is
why so many blockchains all work with solidity because solidity
compiles down to this bytecode here, and polygon, avalanche
arbitrage Aetherium, they all compiled down to the exact same
type of binary, and they all have the exact same readers. Now
why are we telling you all this stuff, you might be saying, hey,
Patrick, this is cool and all but it looks like ABI dot
encode
packed, all that does is concatenate strings, abi encode
pack can do actually way more. And if we look at these global
variables, API dot encode packed is like what the third one down
the list because it's a non standard way to encode stuff to
this binary stuff that we just talked about, we can actually
encode pretty much anything we want to being in this binary
format, basically. And let's take a look at at encoding
something. So let's create a function called encode number.
And this wi
ll be a public pure function, since we're not going
to read any state, and we'll say returns a bytes memory. We're
going to have this function return a bytes object, we're
going to have it returned the what this number is going to
look like but in binary, so we'll say bytes, memory number
equals ABI dot encode one, and then return number. So we're
going to encode number down to it's a bi or it's binary format.
So I know a lot of times when we say oh, what's the ABI what's
the ABI right? Previous
ly, we say, Oh, the ABI is, is this
thing, right? It's, it's all these inputs and outputs. This
is kind of the human readable version of the ABI. But again,
the ABI is the application binary interface we want to
encode or numbers down to it's basically it's binary, this ABI
dot ENCODE is going to be a little different than like the
ABI that you see when you're looking at compilation details.
This is technically like the ABI technically is how to interact
with this contract. However, it's not the
actual binary
version of it. So we're saying, okay, encode this number one
down to its binary version, so that our contracts can interact
with it in a way that they understand. So we're just saying
okay, Watch that number one, let's make you machine readable.
And if we go, we compile this and we deploy this right, let's
delete that that old contract, we deploy this, we now have
combined strings and encode number, we click it, we get this
big hex thing. This is how the computer is going to under
stand
the number one. Now we can encode pretty much anything
actually, we could encode a string. So we'll say, function, encode string, we'll
make this a public key here as well. It'll return a bytes
memory, because we want to give it that binary stuff or that
bites stuff. And we'll say bytes memory, some string equals ABI
dot encode some string, and then return some string. Now let's
compile that delete our old contract, deploy that code
string, we get this big, big, big object here. And this i
s the
binary. Now you'll notice something here, there's a ton of
zeros, and those zeros take up space, right, that's a lot of
space for the computer to take up, even though they're not
really doing anything. They're just kind of taking up space. So
solidity also comes with this ABI dot encode tact, which
performs packed encoding of the given arguments. And you can
read more about it in the solidity docs, if you want. And
this is called the non standard packed mode. And it does the
same encoding
with some stipulations type shorter than
32 bytes are concatenated directly without padding, or
sign extension, dynamic types are encoded in place. And
without the length array elements are padded, but still
encoded in place, you can kind of think of ENCODE packed as
sort of like a compressor, right? It's the ENCODE function.
But it compresses stuff, if we wanted to encode some string but
want to save space. And we didn't need the perfect low
level binary of it. We could do function and code str
ing packed,
make this a public pure, and have it return a bytes memory.
We could say, bytes memory, some string equals ABI dot encode
packed once again, some string. So we're doing a code pack
instead of a code. And we'll return some string here, we'll
compile this and we'll see the difference, right compile, we'll
delete our old one, we'll deploy this. Now we have ENCODE string,
which again, that's what encoded string is gonna give us and we
have encoded string packed, which returns us this muc
h, much
smaller bytes object. So you see the size difference, if we're
trying to save gas, in code, string packed is going to be a
way for us to save a lot more gas. Now, abi dot encode pact is
actually really similar to something that we've done
before, which is typecasting, if we didn't function and code
string, bytes, public pure returns, bytes, memory, bytes,
memories, some string equals bytes, some string, turn some
string, these two are going to look nearly identical, right? So
if we compi
le, we'll delete our old contract, deploy this code
string bytes, which gives us this and encode string pack
using the ABI to encode pack, they give us the exact same
output, whereas encode strings to give us this big piece. So
the two of these get the same result. But behind the scenes,
they're doing something a little bit different. And I'm not going
to go over exactly what that is. But I've left a link inside of
the code here, if you want to learn more, which is exactly
what we're doing in ou
r NFT. Right, we're doing ABR dot
encode packed, we're combining two strings. By putting them
together, we're encoding them to their bytes implementation to
their packed bytes implementation, and then we're
just typecasting them back from bytes to string. And that's how
we concatenate them. Now, at this point, you might be thinking, okay, cool.
Great, Patrick. I'm all set. I understand this. I'm happy to go
back to my project. And if you want to do that, absolutely go
for it and skip over this s
ection, but some other viewers
might be going okay, Patrick, this is seems pretty cool. But
I'm sure this encode pacts in this encode function aren't just
here to concatenate strings, they probably have some other
function, what do they actually do? Well, if that's what you're
asking, I'm glad you asked. And I'm glad you're curious, because
we're going to find out. Now not only can you encode stuff, like
strings and numbers, and really anything, but you can decode
stuff. So I can say, function,
decode string, public, your
returns string memory, string memory, some string equals ABI
dot decode. This is going to take a couple parameters. So if
you look in the docs here, abi dot decode, it takes as a first
argument, the encoded data, and then it takes a tuple. You can
kind of think of it as a list but not quite a list, a set of
types to decode this into and it returns the number of parameters
that you gave it. So we might want to say this string memory
some string Adding, let's give it as
input this encode string
function, the result of this encode string function, right,
which again, is going to be this big thing. So this is kind of
equivalent to sticking this massive thing in here, but we're
just not going to stick the massive thing in there, because
it's really big. So we're gonna say, Let's decode the result of
ENCODE string, and let's decode it into a string. Because we
need to tell solidity, hey, we're going to decode this, but
it doesn't know what to decode it into. It's
like, okay, cool.
I can decode this, but like, what, what do you want me to do
with it, and we say, Oh, this is a string, so decode it into a
string. And then we can do return some string. Now, once
again, we deploy that old Cotters, we delete the last
contract, we deploy this new one. So in code string, and code
string, whereas in code, string, and code string returns this
massive thing, as a human being, we're like, God, I can't read
that computers can read that. But we can't really read that.
So we say, okay, let's decode that back into its string form,
we hit decode string, and we get back some string. And now we can
actually multi encode and multi and decode, right, we can encode
as much stuff as we want. So I can say function, multi ENCODE,
public pure, returns bytes memory, we're going to encode a
couple of things, we'll say bytes, memory, some string
equals ABI dot encode some string, comma, it's bigger. So
we're going to encode two strings here, we're gonna encode
some string,
and it's bigger. So we have two strings, we're going
to encode and we'll return some string, even though it's you
know, bytes. And then we can actually multi decode. So we'll
say function, multi decode, this will be a public pure returns,
we'll say it returns two strings, string memory, and
string memory. And instead of doing string memory, some string
equals API decode, we'll say, string memory, some string,
comma string memory, some other string. So we're gonna get to
returns equals Avi dot d
ecode. Let's decode this multi encode
result, which is the doubly encoded strings into a string
and another string. And then we'll return both of these are
some string, there we go. Now we'll return some string, and
then some other string, I need a semicolon here. So now
when we deploy this, let's close this out, deploy this new one,
right, we now have this multi in code, which gives us this even
bigger bites object, right? Because this is two strings
encoded. And now if we hit multi decode, tak
e a second, what do
you think it's going to put out output? Let's go ahead and hit
it. Now it's gonna give us two strings, right, it's gonna give
you these two strings, some string, it's bigger. So we can
tell solidity to encode a bunch of stuff. And then we can even
decode it by telling it, okay, this big object here, it's two
strings combined, and then we decode it. Now you can even
multi encode with that encode pack thing, right? We could do
function, multi encode packed, public pure returns,
bytes
memory, and then bytes memory, some string equals ABI dot
encode packed some string, comma, it's bigger than return
some string, we could do this, right, but this is going to give
us the packed version of these two strings. So the decoding
actually isn't going to work on this because this is packed
encoding. So if we tried to do, I'm gonna say this doesn't work.
Try to do function, multi decode, packed, public pure
returns, string memory, string memory, some string equals Avi
dot decode m
ulti encode packed in a string and have exactly
what we did above to, if we do return some string, what do you
think's gonna happen? Let's, let's try to delete the old
contract, deploy a new one. We'll do multi decode, packed,
multi encode multi decode, packed, and we actually just get
an error. solidity basically goes, Yeah, this looks like it's
packed. I don't know how to decode that. But instead, what
we can do is we can do function more to string cast packed like
this a public pure returns s
tring memory, string, memory,
some string equals string, multi and code packed, return some
string. This one will work right because again, this pact
encoding is kind of similar to just type casting. So we'll
compile will redeploy multi string cast pact, we get some
Trying, it's bigger, right? And we don't have a space here. But
we should have put a space in there. Now that we've learned
more about this in ABI dot encode and decoding, and we know
that okay, this is what the computer, this is, wh
at
Aetherium. This is what the EVM, or any EVM compatible chain is
looking for. It's looking for this bytecode. It's looking for
this, this binary stuff. And we just learned a little bit more
about how to encode different variables into the binary into
that data bit. Well, what do we do now? Now since we
know that our transactions are just going to be compiled down
to this binary stuff, what we can do then is we can actually
populate this data value of our transactions ourselves with the
binary
that code is going to use. So here's our transaction
for a contract deployment, the data field of the contract
deployment is going to be all that binary code of the contract
for a function call, the data piece is going to be what to
send to the address what data what function to call on the to
address. Let's look at another one of our transactions on ether
scan right on one of our contracts, you don't have to I'm
going to look at enter raffle from a previous section. And if
we select down, we lo
ok at input data. It says function enter
raffle method ID. But if we look at the original, this is what's
getting sent in the data field. It's this binary, it's this hex
it's this weird, low level bytes thing. This is how the Ethereum
blockchain or the or whatever EVM chain you're working with
knows which function to call, it translates this into a function.
And we can do the exact same thing and call these functions
ourselves. So what we can actually do with this crazy
newfound data and coding
stuff, what we can actually do is send
the data field of a transaction ourself, in a transaction call
back in this ether throwback where this data thing was the
contract creation code. Well, instead, we could populate this
data thing with our function call code, the exact function
that we want to call in the binary in hex addition. Now you
might be thinking, Oh, well, why would I do that, I can always
just use the interface, the ABI, all that stuff, well, maybe you
don't have that maybe all you
have is the function name, maybe
all you have is the parameters you want to send. Or maybe you
want to make your code, be able to send arbitrary functions, or
make arbitrary calls or do random really advanced stuff,
right, that's where sending our function calls directly by
populating this data field is going to be incredibly
important. So remember, I said, you're always going to need the
ABI, and the contract address, send a function. Now, when I
said, you always need the API. Originally, we we
re kind of
talking about this thing, this big, this big thing, which is
cool, which is the API. But this is like the human readable API,
you can also do it with the non human readable API. And
additionally, you don't need all this stuff, you can really use
just the name of a function, and then the input types to send a
function call. So the question is, then, okay, how do we send?
How do we send transactions that call functions with just the
data field populated? And then the next question is, h
ow do we
populate the data field? What do we populate the data field with
to make one of these function calls? And then how do we send
these transactions slowly, actually has some more low level
keywords, namely, static call and call we actually we've used
call in the past before, does this code look at all familiar
to you? Well, it should, because this is, we use a similar setup
in our fulfill random words for our lottery, right, we sent
money doing this recent winner dot call, right? Recently,
it
was the address of the recent winner, and we did not call. And
then we have this weird stuff in this brackets here, and then
nothing in the parentheses. So we did actually, essentially, we
use this call keyword previously, but we didn't really
tell you what it did. So call is how we can call functions to
change the state of the blockchain. Static call is
basically how at a low level, we call our view, or pure
functions, write static calls gonna be like, okay, don't
change the state of the bl
ockchain with this one, just
give us the return value. So this is kind of similar to like
a view or a pure function at low level, there's also a Send word
but like, basically forget about it. We're just gonna be working
with call, instead of call. And, you know, later on, we'll learn about another one called
delegate call. But don't worry about that for now. Recent
winter duck call like this, in these little squiggly brackets,
we said, Okay, we updated the value directly of our
transaction in so
lidity. So which again, if we have these
transaction fields, and we just directly updated value in these
little brackets, right, we can also directly update gas limited
gas price in these little brackets if we wanted to as
well. And in here, these parentheses is where we're going
to stick our data. Since all we wanted to do with our
withdrawal. function previously was send money. We said, Okay,
send money change the value that we're going to send. But don't
pass any data, keep that data bit empt
y, which is why, again,
remember how we hit this button before, right and we had called
data be empty, that's essentially running this command
with called data be empty with this section be empty, and then
just updating the value that we set with the transaction. And so
it's this section that we can use to populate data to actually
call specific functions. We're going to put a whole bunch more
comments here. So when our squiggly brackets, we're able to
pass specific fields of a transaction like
value. And in
our parentheses, we're able to pass data in order to call a
specific function. But in here, there's no function to call
since we were just sending them if we want to call a function or
send any data, we can do this in the parentheses. And I think I
spelt that wrong. Now, we've learned a ton here. So let's do
a quick refresher of what we just learned. And then we're
going to actually learn how we can call any function just by
using this syntax here. Well, we learned from really high
level,
if we want to combine strings, we can do ABI dot encode packed
and then typecast that to a string. And in newer versions of
solidity you can do, you can do string dot concat, you know, Hi,
Mom, come on, miss you. In newer versions of solidity. This works
as well, but not in older versions of solidity. Then we
learned a lot about some low level stuff we learned, okay,
when we compile our contracts, we get an ABI file, and this
weird binary thing that numbers and letters stuff that gets wh
en
we deploy a contract that gets sent in the data field of our
contract creation transaction. So for contract creations, the
data is populated with that binary code for function calls
is going to define which functions to call in with what
parameters and this is what we're gonna go over next. Now,
we learned that we can actually encode stuff into this binary
into this low level code. And any program any process that can
read this low level stuff and execute accordingly, read this
EVM stuff, rea
d the specific binary that Aetherium has
specified, or the EVM has specified is considered EVM.
compatible. We can encode numbers, we can encode strings,
we can encode pretty much anything we want to encode. To
save space, we do encode packed, we can decode stuff that we've
encoded, but we can't decode stuff that we encode packed, we
can multi encode stuff, and then multi decode stuff. And then
finally, we can use this call function and add data in here to
make any call that we want to any smart
contract. And this is
what we're going to learn next. Alright, so now's a great time
to take a break, because we just learned some really difficult
concepts. And like I said, if you don't get it the first time, that is okay. Alright,
welcome back. Now that we've learned about this encoding
stuff, let's learn how we can populate this parenthesis this
data field, so we can call any function and we can do
essentially, what the blockchain is going to do at the low level,
we can work with just that
binary, we can work with just
that bytes, we can work with that hex to interact with our
smart contracts. So let's create a new file. And we're gonna call
it call anything, that's all start off with spdx, license
identifier, MIT. And let's talk about this. Now in order to call
now in order to call a function using only the data field of the
call, we need to encode the function name, and the
parameters that we want to add, right, because when we call a
function, we call the function name. And we
call the
parameters. So we need to encode these down to the binary level
so that the EVM, or these Aetherium based smart contracts
and solidity stuff can understand what's actually going
on. In order to do this, we're going to need to work with two
concepts to encode the function name so that the EVM or solidity
can understand it, we actually have to grab something called
the function selector. Now the function selector is going to be
the first four bytes of the function signature. And the
funct
ion signature is just going to be a string, which defines
the function name of parameter. Now, what does this actually
mean? Well, if we have a transfer function, this right
here is known as the function signature. So the function name
is going to be transfer. And it's going to take an address
and a un 256 as its inputs, if we encode this transfer
function, and then we take the first four bytes of it, we get
this, which refers to the function selector. So that's how
solidity knows. So in the byt
ecode, in the binary code,
this function selector is how slitting knows Oh, they're
talking about the transfer function they want me to call
the transfer function. And this is one of the first things that
we need to use call to call any function that we want, we need
to get the function selector and we can get it a number of
different ways but one of the ways is by encoding the function
signature and grabbing the first four bytes. So we'll create this
contract we'll do pragma solidity zero point
8.7 Say
contract, call anything. And we'll give this to stored
variables, give the to stored variables and address public s,
underscore some amount, or some address, and then you in 256,
public s underscore mount. And then we'll create a function
called transfer function transfer. Now normally in here,
we would actually do like transfer for like an ERC 20
transfer, but we're just going to do address some address, and
then you add 256 amount, amount here, we'll make this a public
function. And t
hen all we'll do is we'll set S, some address
equals some address, and then s amount equals amount. So here's
gonna be the function that we're going to work with. And the
function selector for that function is this, the function
signature is this. So it takes an address some address amount,
that gets boiled down to the function selector, and the
function signature. And of course, in our bytecode, there's
going to be some code saying, Okay, here's what this function
does, blah, blah, blah. So we
can actually even write a
function to get that function selector. So we can say,
function, get selector and I'm gonna say get selector one,
because I'm gonna show you a few ways to get the function
selector, we'll make this a public pure. And we'll have this
return a bytes for selector, we could say select or equals bytes
for, and then we hash with a check to 56 of the bytes of that
signature, which is transfer. And it takes an address and a
UNT 56. Right? If we compile this, and then we run it,
let's
get rid of our old contract, deploy, make sure we're on call
anything, if you have the other one up. In here, now we have a
couple of things, we hit Get selector one, we get this Oh X,
a 905, blah, blah, blah, right. And that's the same as the
example I just gave. So this right here tells solidity tells
our smart contract, okay, when we make a call to this contract,
if you see this in the function data, this is referring to our
transfer function with an address and a un 256 as input
param
eter. So we see address you into 56. Our function knows to
execute this data here. Great. And then of course, sml, and S
address are zeros. Now, while we're here, we can also see,
okay, what happens if we call the transfer function, right? It
takes an address and an amount, so let's just give it its own
address or an address. And we'll do 777 for an amount. If we hit
transfer, we have the log up, right, we'll get a little
checkmark here saying success. Now, if we hit S amount, we'll
get 777. And
then the address will be the same, right? So
that's us directly calling transfer. When we directly call
transfer, we're basically saying, hey, grab this function
selector, and then do some other stuff, which we'll we'll tell
you the other stuff in a minute. Now we have the function
selector. Okay, great. What else do we need, we also now need the
parameters we want to add. So we're going to need to encode
those parameters with our function selector. So what we're
gonna do is we're gonna say fun
ction, get data to call
transfer. And in here, we're just going to have this get data
to call transfer, we're going to have it take these input
parameters, and we're going to encode these to work with our
function selector, we're gonna say address, some address. And
if it is x, amount, public pure returns, bytes memory. And then we can return and use
one of those ABI and codings from the cheat sheet. Now, so
far, we've just been doing API and code for a lot of our
encoding. So it since we have t
he function selector, we can
actually do ABI dot encode with selector. This ABI encodes the
given arguments starting from the second and prepends, the
given four byte selector. When we do in Cobra selector, we're
just sticking our selector onto the data that we're going to
give it. So we're going to do return API dot encode with
select door. And we're going to pass it the result of get
selector one, and then we're going to give it some address,
and amount. So what this is going to do, it's gonna
give us
all the data that we need to put in that data field of our
transaction, to send to this contract to let this contract
know, hey, go use the transfer function, pass in some address,
and then an amount. And then if we compile this, we run it,
let's delete our old contract, we'll deploy up, we now got a
new function called Get Data to call and transfer, we'll just
pass you know, we'll just pass this contract address and then
we'll also do 777 again. And so this thing right here is what
we'
re going to put into the data field of our transaction in
order for us to call transfer from anywhere. So this is the
bytes This is the binary encoded data of Hey, call the transfer
function with this address that we specified. with, you know,
777 Mt. So what we can do once we have all this, we can
actually call our transfer function without even having to
directly call it. So what we can do is we can say function, call
transfer function directly, or I guess with binary might be a
better title,
but you get the gist, we'll say address some
address, you in 256 amount, we'll make this a public
function. And we'll have a returns a bytes four and a bool.
You'll see why in a minute. And we'll do that same call thing
that we did to send our raffle money. So what we'll do is,
before we did recent winner dot call, right, we're going to do
some address. And then for us, we're going to address this dot
call. And then we're saying this contracts address which we could
put any address here address
dot call, and we're going to call
the encoded data that points us to the transfer function with
some parameters. So we're going to do address this.com. And we
could just do get data to call transfer address amounts, right, we could
do it like this. Or we could do it kind of the wrong way. We
could do ABI dot and code with select dorm, get selector one,
comma, some address, comma amount. And actually, there's no
semicolon there, sorry. So those are going to be the same. And
this dot call thing, r
ight, it's going to return exactly what we
saw before, it's going to return a bool success. So whether or
not the transaction was successful, and then bytes,
memory, returned data, which is going to be you know, whatever
the call returns. So right, and this is where we put like
require success, right. But for us, we're just going to return
bytes, four bytes for a return datum, and then success. So
we're just going to return the first four bytes of whatever
data we get returned. And then we're go
ing to return whether
this was successful or not. So this function is going to have
us directly call the transfer function by passing these
parameters without us having to do like contract dot transfer,
or, or transfer whatever, right. And you can do this across
multiple contracts across different contracts, just by
changing the address that you call on. So let's go ahead and
compile this. We'll run this now we'll delete our old contract,
we'll deploy call anything. Now if we if we were. So righ
t now
sml, and estimators are both zero. Now, if we do call
transfer function directly, and we'll pass in this one's
address, and then we'll do 777. Now, if we pull up the logs, we
hit this, we're gonna get this transaction response here. But
if we scroll down, we'll ever see the decoded output, which is
a bytes for of just a bunch of zeros, right? Because our
transfer doesn't actually return anything. So it's just gonna be
a whole bunch of zeros, and then our Boolean true, which means it
was su
ccessful. So since it was successful, these two should
have changed based off of that. So let's go ahead and try them
out. And we do indeed see that they're changed. So we have just
directly called this transfer function without having to call
the transfer function itself, we can also do encode with
signature instead of selector. So if we go to our cheat sheet,
there's also this encode with signature down here, which takes
the string memory signature, and it's equivalent to doing ABI dot
encode
with selector bytes for CAC bytes, you know, signature,
it's, it's equivalent to doing exactly what we did up here. But
it does this step for us. So we could copy this whole thing,
paste it down here, right, and we could do, instead of encoded
with selector, we can do encode with SIG netshare, the function
signature, and then we'll copy our function signature from up
here, paste that in here. Compile, we ran into a
compilation error, up, these are the same call transfer function
directly SIG, ca
lled that compile, leader, old contract,
deploy. Now these two are both zeros again. Now if we copy the
contract address, we do call contract call transfer function
directly SIG. We paste that in here, we do 777, we call it, let
me check these we can see that that does the exact same thing.
So this is API dot encode with signature. This is abi dot
encode selector. Encode with signature just turns us into the
selector for us. That's all up here, we just, we encoded this
selector ourselves. Now, t
here are a whole bunch of different
ways to get the selectors. And we're not going to code these
out ourselves. I'm just gonna say a bunch of different ways to
get selector. And who knows why why you might want to use one of
these other reasons, right? There's there's a ton of reasons
why you might want to get the selector a different way. And
here's some now in this video, we're not going to explain or go
over all these different all these different function
selector getting methods. But if you
go through them in the
GitHub repo associated with this course, they all have a ton of
comments to explain what they're doing. Well, we are going to
show you though, it's actually how to contracts can interact
with each other without actually having all the code for each
contract. So we're going to make a second contract that has all
this binary this byte information to call the transfer
function on a different contract. And we're gonna show
you how that can work. This is just another contract
that I've
made called call function without contract. Actually, down
here, we're going to call the transfer function, just by using
the address and the function selector signature and stuff,
we're going to update these storage variables in our call
anything contract from another contract just by doing this
binary calling, if you will, right, so let's compile, it's
going to deploy, we can actually leave this up right, we can
leave this up is let's deploy our call function without
contract, we'll
pass it as an input parameter, the call
anything contract address, we'll deploy it. Now in here, I can
call the transfer function directly by you know, maybe I'll
switch it to this, this contract address this new contract
address, and we'll give it a new number of 123. Right, and we'll
click call transfer function. And then when we go back up
here, we see that this has indeed been updated. Now doing
this call stuff is considered low level. And it's a best
practice to try to avoid it when you can
. So if you can import an
interface, it's much better to do it like that. Because you're
going to have the compiler on your side, you're going to be
able to check to see if your types are matching and all this
other stuff. So usually doing these low level calls some
security auditor checkers might say, hey, like this books been
out a little bit, you doing this low level stuff. But with that being said, You
have just learned a ton about lower level solidity. This is
some really advanced stuff. An
d like I said, if this was hard,
if you're kind of confused here, don't worry, you can always come
back to this section and try it again, when you're a little bit
more advanced, if you want to try to understand it all now.
Awesome. Absolutely, we've left some links in the GitHub repo
associated with this lesson that I definitely recommend you check
out one of the ones you should definitely check out, it's going
to be this deconstructing solidity by openzeppelin, it
really breaks down exactly wha
t's going on behind the
scenes of a contract. If you want to learn more about
opcodes, about low level stuff, definitely give this a read, it
is a phenomenal read, essentially, it breaks down a
little bit more than what we went over here. A couple other
videos as well. And I've left a whole bunch of links in here too. With that being said, here
we are back in our NFT. And now we know all about this ABI dot
encoding stuff, right and what it does, and we know that ABI
dot encode packed, the way we
're using here is just a way to
concatenate strings. And we're not using ABI dot encode for
really any of its crazy superpowers, but we might in the
later section of this course. In other case, so we do use this
base 64 dot encode thing that we've imported, right, we
imported this base 64 dot encode so that we can encode our SVG
that we pass it in to its base 64 encoding. I'm going to copy
paste an example here, you don't have to do this. But like, for
example, we'll pass it in like SVG width eq
uals blah, blah,
blah, all this SVG stuff, kind of similar to what I was showing
you before, we pass that in as an input parameter here, and
output it, we're gonna get the base 64 encoding of it, we're
gonna get this massive kind of string here. We will test this
later to make sure that this works. Normally, if I added a
function in like this right now, I probably would test it right
away. For now we can just leave it in here. That's going to be
great for getting this image here. But we don't wa
nt just an
image, right? We were going to need that metadata. We need this
to be a JSON object, not just an image URL like this, we need
stick this image, this base 64 encoded image into this image
field of our JSON. So how do we actually do this? Well, what we
can do is we know that our ERC 721 code comes with a token URI,
and it's that token URI that points to this, which tells us
what our code is going to look like. So what we can do is we
can actually base 64 encode our JSON as well, to turn
into a
JSON token URI. So we basically four encoded this image to get
this, we're going to stick this URL into our JSON. And then
we're going to base 64 encode our JSON, and that's going to be
the URI that our token uses. So we have our function, token URI,
right. And this takes a un 256 token ID, we'll say it's going
to be a public view, public view override, and it returns a
string memory. So we're going to override the token URI function
of the ERC 721 to whatever we want it to be. And here
we're
going to encode some JSON text that we give our contract into a
base 64 based JSON token URI just to get started we'll do a
require underscore exists. Token ID, and then I'm just gonna say
your I query for non existent token. And yeah, this price
should be an if not exists, Revert with an error. However,
we're just going to go like this. And this exists function
comes in my ear C 721. So we're going to do same thing here. So
we're gonna say require this token, a D exists. And again, we
can
100 percent. And probably should make this an IF EXISTS token,
ID, you know, now what we want to do is we want to figure out
how to make this token URI return a base 64 encoded version
of this JSON. So first, we know how to how to concatenate a
string, right, so that's gonna be the first thing that we're
going to do. So we'll do API dot and code packed. And we're going
to encode ourselves the JSON on chain, we're going to use single
quotes here, because inside of this API and code packed, we're
going to use double quotes in here is where we're going to add
our JSON. So we'll give a name, right, so the first piece of
metadata needs to be a name. So we'll give it a name. And we'll
put a comma here. And we'll say the name of this NF T is going
to be the name we get from, we have a name function, which
returns the name. So we're gonna say the name is going to be the
name. And we're gonna just concatenate all this stuff, name
right there. We're gonna continue on with the JSON. So we
put a
little quote here, and a little quote here. So we
encapsulate this name in quotes, right? Because remember, we're,
we're concatenating, this big string that we're making here,
we'll do a comma, we'll say, description, we'll do another
quote. And this time, we're just going to put the description
ourselves, and we'll say, and NFT, that changes, based on the
chain link feed, we'll put an end quote here and a comma, and
we'll put a comma outside the quote, down here, we'll say,
attributes. We'll ju
st say, trait type, it's
going to be coolness. Comma, value, me 100. Boom, to a comma
to image. We'll put a comma out here. And this is where we're
going to put our image URI, image URI, which we're going to
have to get from somewhere. So for now, I'm just gonna say,
string. Memory image URI equals i,
right, which clearly isn't an image arrived. But just to make
this format and stuff, we'll we'll put this there. This is
where we're going to put that image URI that we get from SVG
to image URI. A
nd then that's it. I mean, close off our JSON,
though. So doing ABI dot encode pack is going to concatenate
this all together. So this is basically going to be a string
that looks like this. Great. But how do we turn this into a base
64 encoded token you arrive so that other people can read it is
we're going to typecast this whole thing to bytes. And then
now that this whole thing is in bytes, we can do exactly what we
did with the SVG above is now we can base 64 encoded. So we'll do
base 64, do
t ENCODE. And then we'll just put another pair of
parentheses around this save and auto formatted. And this here is
going to give us this second line, right, it's going to give
us all of this bit, but it's not going to give us this first bit,
right. So we just need to append to this first bit now. And we
should be good to go. For basics before data image SVG plus XML
basics, the form, this is the prefix for images for SVG
images, right? We use that above because that's the prefix for
SVG images.
The prefix for base 64. Jason is going to be it's
going to be data application JSON base 64. So we're going to
do it like this instead. Now the ERC 721 has something called a
base URI that we're going to override and that we're going to
use. So we're gonna say function underscore base URI. This will
be internal pure. And we're going to override the one that
ERC 721 has. And this is going to returns a string memory. And
we're just going to return this bit right here. And now we can
use this base
URI. to append, right, we're going to append
this first part to our base 64 encoded JSON. So in order to
append them, once again, we'll do ABI dot ENCODE, packed. And
then we'll put this down here. And we'll say, we're going to,
we're going to concatenate base URI to this massive thing that
we just created. And then we save and we ought to format the
now this is obviously a bytes object, and we want it to be a
string. So then all we got to do is typecast it as a string, but
another prints see d
own here, and then we can actually just
return this, but basically what we're doing is we're creating a
JSON string, we encode it in bytes, that way we can encode it
in base 64. Once we have encoded in base 64, which is going to
look like this second string, it's gonna look like here out,
we then just append this initial part. But for JSON objects, it's
data application JSON. We we append that we do API dot encode
packed, and then cast it to string and then boom, we now
have a token URI. That'll
look something like this. And then
all we have to do is update our image URI with what we get from
our function appear, and then we'll be good to go. So let's
finish this out. Let's do this. So in our constructor, we're
passing a low SVG and a high SVG. And what are these low SVG
is in these high SVG as well, basically saying, when the price
of this asset is too low, show a frown. And when the price of the
asset is high, show a smiley face. So we're gonna give it
this frown, SVG, and this happy
SVG as input parameters, low CG
and high SVG, we probably want to save those. But we don't
necessarily want to save them in like their SVG format. So we
just want to store the image URI, right, we will just want to
store this string up here instead of the actual SVG. So
right in our constructor, we can do I underscore low image URI
equals, and we have this SVG to image your eye function, where
we can pass the low SVG. And then we can do the same thing
for the high image URI. So now, SVG image U
RI is going to return
something that looks like this. And we're going to store just
this string this image URI on chain. Now that we have the two
of those, we can use that down below. In our token, Uri
function, when somebody calls token, your I have token ID
zero, we're going to stick into our JSON, either the low image
or I or the high image arrive. And we're actually going to base
that off of the channeling price feed. So how do we do that?
Well, we've already worked with
channeling price fee
ds before so let's go ahead and add it. So
yarn add dash dash dev at chain link slash contracts. Once
that's done at the top, we can do import at chain link slash
contracts, slash SRC slash V 0.8, slash interfaces, slash ag
reg gate, Tor V three interface, that soul like so. And then down
here, let's comment this out. For now, we're going to want to
call a price feed to figure out what the price is and then show
the high image or the low image based off that. So in order to
get a price feed in o
ur constructor, let's just add
another price feed address, street address. And then we'll
make another variable. We'll do aggregate Tor v3 interface.
Internal, immutable, I underscore price feed. And we'll
say in our constructor, I price feed equals aggregate Tor v3
interface at price feed, address. And then what we can do
down here, the old bunch of commas in here into 256 Price
comma, comma comma equals i price feed that latest round
data. And what we can do is we can say if price is greater t
han
or equal to some value, then show one image dry otherwise,
show another one. So we can say String memory, image URI equals
s underscore low image URI. And then if the price is higher than
some value, well greater than the image URI we're going to use
is going to be there's gonna be AI is going to equal the high
image URI. And then we have image right down here. So all we
got to do is figure out the price. So we can make, and in
our mind, we'll let the mentors choose the value that they want
to use. So we can say, into 256, I value and we'll assign each
NFT, their own high value. So we'll need to create like a
little mapping up top, we'll say, mapping, you went to 56.
Two, you went to 56. Let's just make this public for the heck of
it public s underscore token ID to high value. And we'll say
that when they meant an NF T will do s token ID to high
value, s underscore token counter. We'll set that equal to
high value. So when they meant they choose the high value that
they want. And t
hen down here we'll say if the price is
greater than or equal to the high value of the token ID, then
we'll use the high one. Otherwise, we'll just use the
low one. Oops, and this needs to be you into videos x two into
256. Excuse me, since we want to be able to compare them pretty
equally. And boom, our contract looks really good. Now the only
thing we'd want to add in here is probably an event. So we
probably want to emit an event in time we met to one of these
NF T's. So we might do event cre
ated NF T, will say you went
to 56 indexed token ID comma int 256. I value like so. And then
when we met this NFT, will do emit create n t, s underscore
token counter, comma, I value. And then it's best practice to
to have to update our token counter before we actually do
the minting. So we'll do that as well. Okay, there's a ton of
code here. And like I said, we definitely would not have
written all that code without having compiled to run some
tests first, but we decided we want to just write
it all right
off first. So and I did some misspellings. Let's just make
sure everything compiles here. Awesome. Everything's compiled
here. As you already know, a couple things that we're going
to need to do to test this out. First thing we're gonna need to
do is write our deploy function, we've got our basic NFT, we've
got our random and f t, both of these hosted on IPFS. Now we're
going to do a dynamic NFT that's hosted 100 percent on chain, and it
changes based off the price of an asset. So l
et's do this. Oh,
three, deploy dynamic, SVG nft.js. We are no, we're gonna
need a little bit of boilerplate. So let's go to our
basic NFT. And we'll just grab all this the first seven lines
or so. And we'll just paste it in here. What do we need for our
constructor? Well, we need a price feed address, a low
SVG and a highest CG. Okay, so let's get all of those. So price
feed address is something we've already done before. And we can
add that into our helper Hardhead config. And we'll do
one and
a foreign local, we're going to use what we're going to
use a mock. And if we're on Rinkeby, or an actual network,
we're going to use an actual address. So let's go ahead to
dark side chain that link will grab a price feed address, the M
Aetherium. data feeds will go to rank B, rank B and let's just
use eath USD copy that will make a new entry eath USD price feed
like so. And for localhost, we're good. So since we know for
localhost, we're going to need to do a mock. Let's see if we
have a pric
e feed mock. Okay, now we don't, we're gonna need a
mock v3 aggregator dot soul. I just copied pasted mine. If you
want, you can just go right to the to a repo here. Or you can
copy from a previous section, just a reminder heart at f and f
t FCC contracts asked moc v3 aggregator and this is using
point 06 of solidity. So we're going to want to make sure that
in our hard hat dot config, we have at least one 0.6 version,
which we do so we're good there. That means in our deploy mocks,
we're going
to want to add So initial price will be 2000
decimals will be a team. So now we've waited to deploy mocks for
that price feed. So we're gonna say const chain ID equals
network dot config dot chain Id do if development chains dot
includes network dot name, and the more we need to import
development chains looks like where did say const eth USD
akregator equals we'll get that price feed equals awaits ethers
dot get contract Mark v3 Air gate tour, and then we'll up
here we'll do let eath USD price
feed address eath USD price feed
address equals eath USD aggregate tore that address else
will say the eath USD price feed address is going to be equal to
what we find in the network config. Network config. Chain ID
dot eth USD price. Okay, so we have the eth USD price feed
rate. Now we need the lowest VG and the high SVG. So we're going
to create a new folder in our images folder. So we go CD
images, MK dir dynamic NF t. And now we'll have two folders in
here dynamic which is empty and random,
which has all the random
stuff. If you want to use your own SVGs for this, you
absolutely can. But if you want to just come to my images file
and then save these images as so just come right click Save image
as. Save them. and then drag and drop them into your images files
here, you can absolutely do that. So now that we have those,
we want to go ahead and read those into our script here.
We'll say const, low SVG equals a weight. And we're going to use
Fs again. So we're gonna do const Fs equals
require Fs, or
do await Fs dot read file sync, we're going to read in this
file, which for me, it's at dot slash images slash dynamic NF T
slash brown dot SVG. And we're using encoding of UTF eight. And
then we'll say const. Hi, SVG equals await Fs dot read file
sync. Copy this whole thing, because we're using the same
stuff. This one's going to be happy about SVG. And that's it.
So when price is good, we're gonna do happiness, SVG, when
price is bad, we're gonna do frown at SVG. Now, let's go
ahead and let's deploy this contract. So we'll say arguments
or args equals, it's gonna be the price feed address, low SVG,
and then high SVG. And we'll say const, dynamic S, G and F T
equals await, deploy dynamic SVG and f t, comma little bracket
here, from Deployer. args, args log true. And the
weights confirmations, it's going to be network dot config,
that block confirmations, or one will do some logging, I will do
log to do that. But Larry, do some verification, I'm actually
just going to c
opy paste that from our last script, because
it's going to be exactly the same copy paste. But instead of
random IPFS, it's going to be dynamic SVG and f t. The rest of
this looks good. And that's just about it. So we'll do module,
exports dot tags, equals and we'll do all dynamic. SVG. And
we'll do main. Oh, okay. Let's try to see if our deploy script
that we just created works. To do h h, or yarn hardhat deployed
dash dash tags, die Namic SVG, that makes sense, because we
didn't deploy the moc
ks. So we'll do tags, dynamic SVGs. And
then also the mocks. Local network detected play marks, we
deploy the marks, deploying dynamic SVG, awesome. You know
what comes next? You Gosh, darn right, it's time for some tests.
Now, once again, I'm going to encourage you to pause the video
now and try to write your own test for this test for this
section actually can be a little bit tricky. Since we are going
to be manipulating the price of our mock aggregator, we are
checking for these long strings,
and such. So be sure to use the
GitHub repository associated with this lesson, in case you
get lost. Now, I want to show you what this looks like on a
marketplace, like OpenCL. So we are going to deploy this to rink
B. Now keep in mind test that can be slow. So you might want
to be patient here. And you don't even have to do it if you
don't want to. But it is kind of nice to see. Okay, that's what
it really looks like. And you can go to the contract on chain
once it's verified. And you can read
the token URI and
everything. And it's pretty fun. So let's just add one more bit
to our deploy folder. Let's add a mint script that just emits an
NF T for each one of these contracts. So we're going to
create no for mint.js. And we're just going to have each one of
these contracts mint NFT. Let's go ahead and do this. So in
here, we'll do const ethers network equals require arhat.
And then I'm going to do a little copy paste in. I'm just
going to copy this part because I know I'm going to need
that.
We are going to need a deploy but we're not going to need to
deploy. So I'll grab get named accounts, get named accounts,
it's going to come right from there. So we have a Deployer.
Our deployer is just gonna be used to mint them. First we'll
mint the basic NF t. So we'll say const basic NF t equals wait
ethers dot get contract, basic N ft, and we'll connect the
deployer to it. And then we'll say const basic mint and if T or
basic mint, TX equals await basic NF T dot mint NF t. And
then w
e'll do a weight basic mint, TX dot weight one and then
we'll do a little console dot log base Again, T index zero has
token URI, we'll put in a little await basic NF T dot token URI
of zero. That's it for the basic entity. Now we'll do our random
IPFS and empty. So we'll say const, random IPFS NF t equals
await ethers dot get contract, random IPFS and f t, connected
to the Deployer. This one, we need a mint fee. So we'll say
const, mint V equals await random IPFS NF T dot get mint
fee. And then
we'll do the mint. So we'll say const, random IPFS
and ft mint, TX equals await, random IPFS and F T dot request
and F team. And for this one, we need to pass a value, which is
going to be the mint v.to string. Now for this one, just
like what we saw in our tests, we're going to have to do this
await new promise again, right, because we need to wait for it
to return need to listen for those events, we probably should
set up the listener first. So let's actually set up the
listener first. So we'
re going to do await new promise, and
we're gonna do async function. And we're going to do resolve
reject. In here, we're gonna use that, that fun little arrow
syntax in here. And now since we're in this
function here, we actually set the timeout resolve like this,
which means we have five minutes to time this out, you might want
to bump this up even more five minutes might not be enough,
this is gonna be 300 milliseconds here, we're gonna
do that once again. So we'll say random IPFS and ft dot
once.
Once we get that NFT minted event, we're going to run an
async function, we're just gonna do resolve. And inside here is
where we can actually put, actually requesting the NFT. But
below our listener, right, so in there, and then we can say if
developments chains, that includes network dot name. So
let's just make sure we import those development chains and
network or affect them and chains that includes network dot
name. So we're on a test net, this is where we go ahead and we
pretend to
be those mocks. So we'll say const Request ID
equals random IPFS. Oh, actually, we're going to need to
do const random IPFS and ft min TX receipt equals await on IPFS
and empty minted TX dot weight one. So we're gonna need to get
the receipt. And from the receipt, we can get the request
ID. That events one dot args dot request id.to string. And then
we can do const VRF. coordinator V to mock equals await ethers
dot get contract, the RF coordinate for V to mock,
connect this to the Deployer. And
then we'll do a wait the RF
corded and a Tor V to mock that fulfill random words with
Request ID, random IPFS. And if T dot address, we can do console
dot log random IPFS and have T index zero token URI to await
random IP. That's NF T dot token URI of zero. Finally, we can do
our dynamic SVG, NF t. So we can say const, high high value
equals ethers.utils.do parse ether here. So we'll say
4000 $4,000 will be the high value. We'll say const. Dynamic
SVG and f t equals await ethers dot get contract
, dynamic. SVG
and a T will connect it to the deployer say const by Namic, s,
B, G, and f t mint, TX equals await dynamic SVG and F T dot
mint and f t i value to string then we'll just do wait. This
stat wait one. And finally console dot log dynamic. SVG NFT
index zero token URI is going to be a weight dynamic. SVG and ft
dot token URI of zero. Okay, I think that looks good. Let's try
this on a local network. So we'll do yarn hard hat deploy.
And we'll run all those scripts It looks like everyth
ing worked.
So we have random, basic NFT index zero has a token year, I
have this IPFS thing, random IPFS NFT. index zero has this
thing. And then our SVG has this giant monstrosity, okay,
perfect. And then we can even check, right, we can even grab
this IPFS hash, we go to our IPFS node, or if you installed
IPFS in your browser, or you're working with Brave, we can just
pop it right into our browsers and see what it looks like.
Right. And then if I zoom in, and adorable St. Bernard, with
the im
age of the St. Bernard, looking like that, this one's
also gonna be St. Bernard. And then of course, are SVG, which
we can also copy, paste, and boom, that looks great. And then
we can copy the image. And it's a frowny face or shad. But
Awesome. Okay, so it's working locally for us. Now, let's go
ahead and try to make this work on an actual test net. So
hopefully, our helper hard hat config is set up correctly. And
there's enough stuff in here where we need to make sure that
we have a subscripti
on ID, right, we're going to need to
make sure we have a subscription ID. And we shouldn't call the
mint function, right, because we're going to need to add our
consumer to the VRF. Before we can actually meant so let me
let's add some tags to our meant here. So we'll do module dot
exports, dot tags equals, and we'll say all comma meant a
while ago, I said okay, let's add a main tag. Now we're coming
around to why we added this main tag here. So what we want to do
is want to deploy all of these
contracts. But before we finally
meant for our IPFS one, we need to add that contract to our
consumer, here's what we're gonna do, we're gonna run yarn,
Hardhead, deploy dash dash network Rinkeby dash dash tags
main. Now, this won't meant any of our NF T's, okay, won't mean
to any of our NF T's, it'll just deploy those contracts might
just sit around and wait a little bit for these to actually
deploy. So this is a great time to go take a break, maybe go
take a walk, get a sip of water, get a cup
of coffee, whatever
you want to do. Yeah, once everything is deployed, then we
can go to V or F dot chain dot link, we're already connected
here, we used our subscription. And then we would just add our
IPFS consumer in here. And we'd be good to go. And All right,
once everything goes through, and we have all three of our
transactions on the blockchain, we can go ahead, we can grab our
random IPFS NF T, we'll grab that address, we'll come back
over to V or F dot chain dot link slash rink D, we'
ll go to
our subscription ID, and we'll add a new consumer will add that
contract address. So we'll go ahead and approve and Metamask.
And once this goes through, we can finish running the mint part
of our deploy folder. Once it's confirmed, we can close maybe we
can do a little refresh, we should see our new address added
as a subscription here. Now that we've added that we can mint one
NF T from each one of these contracts are in hard hats,
deploy dash dash tags meant dash dash network Rinkeby
. And we'll
have to wait a little bit for this too. Okay, now that we have
them all minted, we should get a little output like this. Right?
Basic NF t zero has token URI here. Basic random IPFS NF T has
token your eye here. And then our SVG has this as a token
year, right. So what can we do now? Well, let me go grab, I'll
grab my wallet address and stick it into Rinkeby ether scan. And
we can see we called mint request and mint again. And we
created three contracts, right, we created our basic N
FT, our
random IPFS NFT and our dynamic SVG NFT. What we can do now is
we can grab, we can copy the address of our contract. And we
can go to test nets that open see that i Oh, and we can put that address in
the bar here in the search bar here. Now this part is
incredibly, incredibly variable. Okay, open C can be really slow.
And it can take open see up to a couple of hours to register that
a contract has been deployed to a test net. So if it doesn't
show up right away, don't be discouraged. Don
't let it drag
you down. But if it does, you should be able to click on your
collection and see the NFT is actually here. I'm going to grab
our random IPFS NFT. Let's grab that contract address. I'm going
to grab that test net site open c.io. Paste that address in
there. And what do you know we do indeed see, random IPFS and f
t, right and I've deployed a couple of them. So this one's v
two, and we have our adorable Shiva in you right here. So this
is what it looks like an open C now we can 100
percent verify that our
code is good even if it doesn't show up on Open. See. If we go
to the contract we go to read contract. And then we go to
token URI punch and zero here. A query, grabbed this, stick it
into our browsers, the JSON looks good. So let's grab the
image URI. Paste that in. And if we can see this here, that means
that our code is good. And you have successfully deployed a
number of FFTs to the blockchain. We have learned a
massive amount in this course, this is definitely one of
the
most jam packed one. And it's all about art. Right? Isn't that
crazy? Let's do a quick refresher of this entire course
here. So first off, we learned the basics of an NF T with our
basic NF T dot Sol, we learn that these NF T's are based off
of the ERC 721 standard. And that just means they have
functions like name, token, Uri, etc. We learned that NF T's use
this token URI to tell us what the token actually looks like a
token, your eye will look something like this, it's going
to be a name
a description, it's going to have an image URL,
which points to a different location for what the NFT
actually looks like. It'll have stuff like attributes, it can
have stuff like attributes, and a few other tags. This is known
as the metadata of the NFT. And this tells us about the NFT. We
can also have all that metadata on chain, of course, to
customize it on chain, and make it look and grow and change and
be interactive on chain, we learned more about IPFS, we
actually wrote a script called
upload to pinjarra.js, where we
can actually programmatically upload images and files to
another IPFS pinning service for us, we can of course, always use
our own IPFS nodes if we want. Now this token URI can really be
anything. And we hosted it on IPFS for our basic NFT and for
our random NFT. But for our dynamic NFT, we actually hosted
the token, you're right 100 percent On chain, so we didn't use IPFS.
And we made this dynamic where the token URI actually changes
based off of the price of a c
hangeling price feed in our
random IPFS NFT, we gave our NFT a chance we gave different
rarities to the different dogs so that we could create
programmatically rare NF TS where our pug is super rare. Our
Shiva is sort of rare, and our St. Bernard is pretty common. So
the fact that we got to ship it in you was awesome. We did some
amazing deployments, we wrote some tests. Not only that, but
we learned a lot about transactions, and how we can
actually add whatever data we want to this data section
. And a
little bit more about what our transactions look like, and how
we can actually use functions, selectors and function
signatures to be able to call anything, right and we learn
more about ABI dot encoding, and encoding packed and all this
binary stuff if you want it to go deep into that. So this was
an absolutely JAM PACKED session. And you should be
incredibly proud of yourself, especially with your little
puppy that you can see on open sea or you can see directly on
ether scan, or you c
an just look at it and IPFS and be really
proud of what you've done. But with that being said, huge
congratulations on making this far. Definitely definitely
definitely take a break here, and we'll see you in the next
one. Okay, now we have less than
fifth team, which is going to be our next JS NFT marketplace. And
if you finish this lesson, you are a web three full stack
monster, this is going to be our most complicated front end using
the web three stack and using a lot of really advanced web
three, and blockchain tools. So get really excited because we
are going to learn a ton in this lesson. Now there are actually
three different repos associated with this lesson. The first one
is going to be our typical hard hat project. For the back end,
after the hard hat project, we actually have two repos both are
going to be our front end repos, and they're going to be slightly
different. In this project, we're going to learn more about
how events are so important and why events are so import
ant,
especially for off chain services. And so we're actually
going to look at two different ways to work with them, one
using the mirallas, or a centralized database, and then
one using the graph. And the reason that I want to show both
of these is that oftentimes, when people are looking to scale
the projects, when people are looking to get things done
really quickly, taking a more centralized approach can often
be a little quicker, and you can sometimes add more functionality
to your website.
And there's still a lot of protocols that
have decentralized backends. And centralized front ends. One such
example, is open sea, open sea, for example, has the ability to
actually like different and fts. Now, this isn't something that
we would actually want to spend any gas on. But it is something
that we're gonna have to store in some type of database
somewhere, so that people have the ability to do that. So I
want to show you this optional first way to build these front
ends, since all of ou
r logic is still going to be 100 percent. On
chain, the front end matters a little bit less, because anybody
can still interact with the contracts that we build on
chain. Now in web three, we don't want to stay there.
However, getting an MVP done getting a minimal viable project
done is really, really important. So using a
centralized server, like Morales, or centralized project
can make us much quicker. In fact, we have been using
centralized services, like alchemy, kind of throughout this
whol
e project. But of course, I also want to show you the
decentralized way to make your front end. So after we work with
Morales, we're also going to show you how to use the graph
then for to do all this event indexing. Now the graph is going
to be the decentralized way we can make our front end and work
with these events. And the graph also comes with its own graph
repo. So we'll learn all about that once we get to the front
end section. But let me show you what we're going to build
because it is
really cool. Now that we've learned a ton about
how to make NF T's what they are, we're going to make our own
NFT marketplace. And like I said, this is really going to be
our deep dive into all these amazing front end tools. So
here's what our front end is gonna look like. But what we can
do is we can connect with our little connect button, we hit
Metamask Metamask pops up, we'll go ahead and connect. And now
that we're connected, we can see the different entities in here.
And if we're on an add
ress that's owned by us, it will say
owned by you. And if we switch addresses, or UI will go ahead
and update, connect there. And now we're owned by a different
address. Now, if it's owned by us, we get this little hover
that says Update listing. And right now it's worth 0.18.
That's what it's listed for on our marketplace. If it's owned
by us, and we click it, we can update it to a different price.
Let's update it to 50 eath, or whatever your layer one currency
is, we'll just say new listing pr
ice, we'll go ahead and
confirm. And I'll say listing updated, please refresh, and
what we can do them. And we'll mine some blocks on the back
end, and boom, now we see that it's worth 50 here. Now if we
switch to a different account, now we can see owned by blah,
blah, blah, and the hovered now says by me, if his selected as a
different user, I'm gonna get this transaction to actually buy
it. Now go ahead and confirm that I'm gonna buy it, I get a
little pop up. This is item bought successfully
. Now if I do
a little refresh, we'll now see that that NFT is gone from the
marketplace, since we bought it right, it's no longer available
to be sold. Now what we can do then is we can come over to sell
NF Ts and at the bottom, we'll see a withdraw proceeds. So
whenever somebody buys an NF T, the NFT marketplace actually
keeps the proceeds that actually keeps the result of the sale. So
if we switch back to our address that had the NF T listed, we can
now see Withdraw 50 proceeds because we kno
w that we have 50
eath, because we just bought that for 50. So if we hit
withdraw, Metamask is gonna pop up, we can go ahead and confirm,
wait a little bit as transaction populates, and boom once it goes
through, we'll see now we have zero proceeds. Right, we
withdrew everything from here. So what we can do now, is we can
relist that NFT. So if we come back, let's go back to the one
who just bought that NF t. If we know the address and the token
ID of the NF t and we own it, we can go ahead and
relist it,
because we can place the address in here with the token ID Give
us some sort of price, we'll submit, we'll approve giving the
NFT marketplace access to our NFT to our little doggie. And
then we'll go ahead and actually send the transaction to actually
list the NFT on a marketplace that we get NFT listed
successfully. After we remove some blocks in the back end, we
can go back to the front end. And we now see, it's owned by
us, instead of the original owner, right and set for 10
ether.
And then we can of course, switch back to a
different user, and we can have them actually buy. So this is
going to be a NFT marketplace that's completely decentralized,
we are going to learn a ton about front end a ton about
indexing a ton about events, and why they are so powerful. And
I'm really excited for you for this one. Because if you get
through this one, you will have so many tools at your fingertips
for working with the blockchain. Are you ready, let's jump in.
Let's build the contrac
ts first, and then we'll build the front
end. Let's do this. Now this project is going to be based off
the Arteon project, which is a completely open source
decentralized smart contract NFT marketplace, I'll leave a link
to it in the GitHub repo associated with this course,
ours of course, is going to be a minimalistic version of this. So we're in our VS code pre
normal. And we're going to create a new folder here called
hard hat. And if T marketplace, FCC, oops, MK dir, like them,
we're gonna c
d into it. And then open this up and its own VS
code. Once again, you can use code period, or file open
folder, and open this folder. Once we get in here, we're going
to do all of our normal stuff that we've been doing throughout
the course. And once again, if you want to copy paste over your
package, JSON, if you want to copy paste in the modules,
whatever you want to do, feel free to do so I'm gonna go ahead
actually, this repo here, we're gonna scroll up and just grab
once again, this line fr
om less than nine. And just run that I
know I'm gonna be using prettier so I'm just gonna go ahead and
copy paste those two prettier files over prettier ignore and
prettier RC going to be using them again, for linting. With
solidity we're going to use Sol hint dot JSON. So we're going to
grab that dot soul hint dot JSON. And the dots will hint dot
ignore. I'm also going to grab the hard hat dot config dot j s
because we're going to be using a really, really similar setup.
And this hard hat dot c
onfig dot j s. It's got waffle in it,
ether scan, hard hat deploy coverage gas reporter sizer and
Dottie and v dot config, we're going to bring over our Dotty
and V. And we're also gonna bring over our utils folder as
well. Right, so a lot of that boilerplate we're gonna bring on
over. And now just like that, since we have the hard hat dot
config dot j s in here, if we run yarn, hard hat, right now,
yarn hard hat will actually see we get the output like this. So
let's go ahead. And before we act
ually write our contracts,
let's go ahead and write a little doc saying what our
contract is even going to do, what do we want this to do?
We're going to create a decentralized NFT marketplace.
So what does that mean? What will we probably need? Well,
we'll probably need some type of list item function, because
we'll want to list NF T's and this will be to list NF t's on
the marketplace, we'll need some type of buy item to buy the NF
T's. And then we'll probably need maybe like a cancel listing
or cancel item, if you no longer want to sell it, maybe an update
listing, update price. And then maybe a withdraw proceeds to
withdraw payment or my bot and fts. So when somebody buys an
NFT, I'm gonna have to withdraw it from the contract since the
contract is going to be the one to actually hold those funds.
That looks pretty good to me. Let's go ahead and start
building this. So let's create a new folder, contracts. And let's
jump into this. So we'll create a new file NFT marketplace. That's
all. So let's get our
boilerplate. spdx pragma, solidity, carrot zero, point,
8.7, contract and ft. Marketplace, boom. If we're
doing this, right, h h compile or yarn Hardhead to compile or
MPX, art, hit, compile, boom, things are looking good. So if
we go back to our readme, we can grab these here, even stick them
in as like a little comment for us to kind of reference later
on. Let's start with listing the items. How are we going to keep
track of listing people's items. And once again, rememb
er, when
I'm usually coding this, I'm going back and forth between
writing tests and writing the actual code. We're just going to
write all the solidity in one chunk, and then go write the
tests. So we're going to say, these are going to be our main
functions. I'm going to start with function list item. And we
are going to make this one look really, really good. So we're
going to do natspec. And everything, this is going to
need to be an external function, right, we're probably not going
to want
any of our internal functions calling list item,
it's going to be called by external projects or external
accounts are probably going to need an address and ft address,
write the address of the NFT, contract a un 256 token ID, the
ID of the token ID of the contract that we're going to
use. And then we're going to want to set a un 256 price. So
first off, we're probably going to want the price to be greater
than zero. So maybe we'll put in like a little if or require
statement here, we'll say if
price is less than or equal to
zero, then we'll go ahead and revert with a price must be
above zero error. And then of course, we'll prepend it with
the name of the contract into underscores. And then at the
top, the error price must be above zero. Now in order for us
to list it, we could actually do this one of two ways we could
one, we could send the NFT to the contract, this would require
us doing like a transfer, right, we could have got the contract
hold the NFT. Now we could do this, but
this is going to be
kind of gas expensive for someone to actually list on f t.
And we can have the owner of the NFT be our NFT marketplace, we
could 100 percent do that. The issue with this, though is that the
marketplace will then own the NFT. And the user won't be able
to say like, Hey, I own this NFT, it's in the marketplace,
they technically would be able to but they would have to
withdraw it, we might do this a slightly different way where we
can say owners can still hold their NFT and give
the
marketplace approval to sell the NFT for them. Now, of course the
owners of the entity could withdraw approval at any time
and the marketplace wouldn't be able to sell it anymore.
However, this would be really easy for people to actually
read, they would all they would have to do is read like is
approved for marketplace. And they can actually see if the
item was really listed or not. So we're gonna go ahead and
write it this second way, because that's what Ardian does.
And this is the least
intrusive way to have this marketplace,
right? People still will have ownership of their NF Ts, and
the marketplace will just have approval to actually swap and
sell their NF T once the prices are met. So since we want to
make sure the marketplace has approval, let's make sure the
marketplace has approval. So we can call we can call this get
approved function on that token ID to make sure that the
marketplace is approved to work with the NFT. To do this, we're
going to need the AI ERC 720 inter
face and we can actually
grab that from open Zeppelin. Right and this interface will
wrap around an address and then we can call get approved on that
address. So we'll do import at open Zeppelin slash contracts
slash token slash ERC 721 slash I ERC 721 dot Sol. And since
we're doing an import from open Zeppelin, we'll do yarn add dash
dash Dev, add open Zeppelin now that we have this interface in
here, what we can do is we'll say I ERC 721 NF t equals IRC
721 wrapped around this NF T address tha
t we're passing in.
And we'll say if NF T dot get approved of the token ID that
we're trying to list does not equal address this. So if we are
not approved, then we'll revert not approved or market place.
And then we'll of course we'll want to do prepend it with NFT
marketplace into underscores. So error like this, Bada bing bada
boom, now that we've gotten a little bit of that out of the
way, we're probably going to want to have some type of data
structure to list all these NF T's. And typicall
y we get to
Okay, do we want to use an array? Or do we want to use a
mapping? What do you think? Before we continue? Let's pause
for a second, do you think it makes more sense to put these NF
T's and an array or an A mapping? And when you're
thinking about this, try to think about, okay, well, people
are gonna have to buy these and sell these, what makes more
sense, think about this for a second, maybe pause it and write
in a comment here, what you think an array or a mapping is
better. Now, if
you said mapping, I would agree with you.
You couldn't do an array and you wouldn't necessarily be wrong,
but it's not the way that I would go about that for an
array. Anytime someone wants to buy an item, we're gonna have to
traverse through the array, we're gonna have to make this
massive dynamic array. And that might get a little bit dicey as
that array gets really, really big. So we're gonna go ahead and
make this a mapping. And this is probably going to be a global
variable or a state varia
ble. So up at the top, let's go ahead
and create this mapping, it's going to be a mapping of
addresses of NFT addresses. Right? So it's going to be the
NFT contract address mapped to the NFT, token ID mapped to some
type of listing. So we'll say, a mapping of address to a mapping
of UNT 256. to, well, what do we want here? Well, we want we want
the price, right? So is that another you went up to six. But
we also want, we also want to keep track of the sellers, we
know who to send money to. So we
could make two mappings or we
just create a new type of type listing, let's go ahead and do
that. We'll comment this out for now. And so at the top, since
this is going to be a type where, say, struct listing, and
in here, we're going to do a utility six, the price of the NF
t, and then address the seller of the NF team. And now that we
have that new typing, we can uncomment this, we can say NFT,
contract address map to the NFT token ID mapped to the listing,
and we'll make this a private varia
ble called S underscore
listings. Now back down in our list item function, we're going
to update that s listing mapping. So we're gonna say s
listing of NF T address, right, the address of the NF T at the
token ID is going to equal we're gonna create a listing of the
price. And then who, well the seller is going to be message
that sender, right? So message dot sender, they're the one
who's actually listing the item. And since we're updating a
mapping here, what's the best practice for update map
pings,
you guessed it, we need to emit an event and especially for this
project, you're gonna see why emitting events, for at least
this project, this is so helpful. So we're gonna go ahead
and emit an item listed event, which we're going to create in
just a second. And we'll give the message that sender, the NFT
address, the token ID, and the price item listed. And then up
at the top, of course, but below our structure, we're gonna say
event item listed will do an address indexed seller address
indexed NFT, address,
address indexed token ID, and then a un 256 price. Sorry, this
needs to be a un 256 token ID, you int 56. This looks pretty
good to us. However, we probably want to make sure we only list
and FTEs that haven't already been listed. So we can add like
an if then in here. And this is kind of where preference comes
in a little bit. But I'm actually going to create a
modifier called not listed. So we make sure we don't relist,
and if T's that are already listed above our main f
unctions,
or do like a little indicator modifier not listed. This is
gonna take an an address NFT address, a un 256 token ID and
an address owner. And what we're going to say is we're going to
check, we're going to make a new listing memory listing equals s
underscore listings of NFT address token ID. Now we're
gonna say if listing dot price is greater than zero, we're
gonna go ahead and revert with already passing the NFT address
and a token ID. And of course, we're going to prepend this with
N
FT marketplace. And at the top, we do error ft marketplace
already listed, like so. And then we're going to put a little
underscore right underneath and then up here, we'll do address
and if the address you went to 56. Okay, ID. So this modifier
looks pretty good. Let's just make sure it's actually going to
compile we'll do yarn Hardhead compile or hh compile. Great.
That looks good. We'll add this modifier to our list item
function will do NFT address, token ID message dot sender.
Cool. What el
se should we check for here? Well, we should also
check that the NF T that's being listed is owned by message dot
sender. This way only the owners of the NF T can actually listed
here so let's go ahead and we'll add a is owner modifier modifier
is owner NFT address token ID spender you into 256 here and an
address spender I ERC 721 NF t equals IRC 721 And if the
address address owner equals NF T dot owner a Have a token ID.
And then we'll say if spender does not equal owner. And we'll
revert wit
h a not owner error that we're going to go ahead and
create up top. So we'll say error, not owner. And we'll
prepend it with NFT marketplace with two underscores revert
revert not owner, then will do underscore for the rest of the
code, and boom, now underneath are not listed will do is owner
NFT address, token ID message dot center. So now our list item
checks to see if it's already listed, make sure that only the
owner of the NFT of that token, Id can list it. And then it goes
ahead and lists
it looking nicely. Okay, cool. So that is
our list item method here. Now let's go ahead and do a little
bit of natspec on this. And now we have a little
natspec here, which looks really professional. Alright, great. So
we have a list item function. All right, what's next? Well,
maybe let's make a buy item function for people to buy their
NF T's after they've been listed. So let's create them do
function by item. So we'll take an address, and if the address
un 256 token ID, and this will be an ex
ternal function. And
that will also make this payable an external function because we
know only people or contracts outside of this contract are
going to call by item and payable so that people can spend
eath to spend eath, or whatever layer one currency to actually
buy these prices, we could want 100 percent at channeling prices in now
for listing, we could of course, add price and then do like, you
know address token price. And do what we did before with chain
link price feeds to convert the p
rice of these tokens into how
much they actually cost. And we could 100 percent do that with chaining
price feeds. But for simplicity, we're gonna leave that off. But
I will put that as a challenge to you through challenge is
going to be have this contract accept payment in a subset of
tokens as well, of course, we would need to give a little hint
here is channeling price feeds to convert the price of the
tokens between each other, we're gonna choose which NFT in which
token ID we want to buy. S
o what's the first thing that we
probably want to do? Well, we probably want to check that this
by item is actually listed. So we're actually going to make a
new modifier instead of not listed we'll make it is listed
up and modifiers modifier is listed. And this is going to
take an address and up the address un 256 token ID. And to
check to see if this is listed, we'll say listing memory listing
equals s underscore listings of the NFT address of the token, Id
sort of go into the mapping here. An
d then we're just gonna
check the price. So we'll say if the listing that price is less
than or equal to zero, so basically, if there's no price,
if it's defaulted to zero, if the price is zero, then we're
gonna say revert, not listed. And after the address, token ID,
and of course, we're going to prepend. And if the marketplace
and the marketplace underscore underscore, not listed, and then
we're gonna copy this up here, we're gonna say air listed, and
this is going to take address, NFT address
, and au int 256
token Id like so, the down in our modifier, then we're going
to add the underscore and to add the rest of our code here. So
now we have an is listed modifier, we're going to check
to make sure that that NFT is actually listed down here now
we're gonna say is listed address and ft address or excuse
me, and if the address and token ID. Now once again, we're gonna
say listing memory listed item equals s underscore listings.
And if T address token ID, or say if message dot value is
less
than listed item dot price, then we're going to revert with price
not met. Then we'll do NFT address, Open ID listed item dot
price Excel so we're going to create a new air price not met
error. Price not met of course we're going to prepend this with
NFT marketplace to take an address. And if the address you
went to 56 token ID and then a UNT 256 price. So we can see exe
exactly how the price wasn't met, and then back down here,
we'll get the full error. So we want to make sure they're
send
ing us enough money. First of all, when they send this
money, it needs to belong to whomever listed the item. So we
actually need to keep track of how much money these people
have. So let's create another data structure called proceeds
where we keep track of how much money people have earned selling
their NF T's. So we'll create a mapping of address to you and
256. And this is going to be a mapping of seller address to
amount earned. And we'll make this private called S underscore
proceeds. And
what we'll do is when somebody buys an item, is
will update their proceeds. So we'll say as proceeds of
listed item, that seller equals s proceeds of illicit IO dot
seller plus MSG dot value. Now, once we buy this item, we're
going to want to delete the listing. So to delete a mapping
from a wreck, so to delete an entry and a mapping, we just use
delete s underscore listings and ft address of the token ID. So
we remove that mapping. And then finally, we're going to go ahead
and transfer it. So w
e'll say I ERC 721 and ft address, we're
going to call dot transfer from the listed item dot seller to
the message dot sender with the token ID. Now you'll notice
something here, we don't just send the seller the money. Now
why is that? Well, solidity has this concept called pull over
push. And it's considered a best practice when working with
solidity, you want to shift the risk associated with
transferring ether to the user. So instead of sending the money
to the user, this is what we don't wa
nt to do want to have
them withdraw the money, we always want to shift the risk of
working with money and working with eath or whatever layer one
you're working with, to the actual user. So we don't want to
send them the money directly, we want to create this s proceeds
data structure and we can have them withdraw from it later on.
Now, we could probably do some checking here. Or we could say
okay, check to make sure the NFT was transferred. And if we look
at I ERC 721 though, and we're looking
at the transfer from
function, we don't see it actually has a return. And if we
go to the IP 721 We can see that none of these have a return type
though, transfer from doesn't have a return type here.
However, we do see this safe transfer from bit safe transfer
from it's going to be a little bit better, right because if we
look at transfer from transfers ownership of an entity, the
caller is responsible to confirm that underscore two is capable
of receiving entities or else they may be permanent
ly lost. So
maybe instead we want to use safe transfer from which throws
an error unless message sender is the current owner and
authorize operator, or blah, blah, blah. So instead of
transfer from we're going to actually use safe transfer from
just to be a little bit safer. So we'll do safe transfer from
instead of transfer from. And then since we're updating a
mapping, we're going to do what, you guessed it, let's omit an
event, we'll call item bot. Lab will be a message that sender,
an empty
address, token ID and for listed item that price. So
off the top, let's create a new event. To event item bought. And
this will be a an address indexed fire an address indexed
NFT address an address indexed token ID and then a un 236
price. Just kidding, that doesn't look fantastic. This
should be intuitive six. Now it looks fantastic. Now in this buy
item, we've set this up in a way that is safe from something
called a reentrancy attack. And we've been coding these
contracts in a way where we k
ind of do all this state change
first. And then we transfer the NFT that token or etc. But why
are we doing that? Cognitively we think it might make sense.
Okay, first, maybe we should actually send the NFT right,
we'd want to send the entity first. This is actually a huge security
vulnerability. And to understand why let's learn about one The
most common hacks in blockchain, the reentrant. See attack. Now
in this sub lesson, we're going to talk about reentrant. C, and
in the GitHub repo associa
ted with this lesson, we're going to
have the code for everything that we're going to go through
here. And the code that we're looking at is based off of this
solidity by example. reentrant. See example. And I have a link
to it in the GitHub repo associated with this course.
Now, I have a sample contract here, it's a place where you can
deposit and withdraw your eath. So what it does is it has a
mapping called balances, where you can call deposit, and it'll
update how much you've deposited into
the protocol. And then it
has a withdrawal function as well. So what it does is it
first grabs your balance from this balances mapping, make sure
that you have more than zero. And then the way that we've been
sending eath, this whole time, we do message that sender dot
call, we send the balance, and then we update bounces of
message sender equals zero. Now this is the line that actually
makes this contract incredibly vulnerable. And if we run this
right now, though, we'll say hey, no, it looks l
ike it's
working as expected, we can go to deploy a copy the accounts
that I'm working with, like that in a bounce zero, we can
deposit, you know, it's going away, let's go to ether, will
deposit to ether, come down, what deposit now had to balance
bounces up, we'll hit withdraw, now have bounces goes back to
zero. And it seems like it's working as intended. Now,
there's actually a way we can attack this function to drain
all the money in this contract. And this is what's known as a
reentrancy a
ttack. The two most common kinds of attacks in this
space are going to be reentrancy attacks, which is what we're
talking about here. And Oracle attacks, which usually only
happen when a protocol doesn't use the decentralized Oracle,
lucky for you, we're teaching you right from the get go how to
use chain link so that you can be protected. And it's these two
types of attacks that often result in the most amount of
money last, there's a leaderboard called rec dot news,
which keeps track of many o
f the top attacks that have ever
happened in the defi space. With many of them if you go into the
retrospectives are either an Oracle attack or a reentrancy
attack. And you might be saying, Hey, where are we just talking
about NF T's this, this doesn't have anything to do with NF T's
we'll get there, don't worry. In a new contract below, we're
going to create a new contract called attack down here. And
what we'll do with this attack contract is we'll grab this
reentrant vulnerable contract, we'l
l say reentrant, vulnerable,
public reentrant vulnerable, like so. And we'll save that
reentrant vulnerable contract as a global variable. And we'll say
construct door address, underscore ranch and vulnerable
address. And then we'll say reentrant vulnerable equals
rancher and vulnerable at reentrant vulnerable address.
Now what we're going to do is we're going to create a function
called attack. And it's this function that's going to call
withdraw in a malicious way. So we're going to say attack
, this
is going to be an external payable contract. And we're
going to call the posit on this. So we'll deposit some money
first. So we'll do reentrant vulnerable dot the posit will send a value of one ether.
And then immediately we will call re N Trent vulnerable dot
withdraw. Now at first glance, this seems pretty harmless. But remember, when we call
message dot call like this to send we're calling back to this
this attack contract. Now when we call this attack contract, is
there a way to exec
ute any other code? Well, there is remember
how we learned about fallback functions. If we put a fallback
function in here or a receive function. When this code runs
call and seconds our contract ether we can have it trigger our
fallback function to call withdraw again. So that will
send our contract more ether than it's do before we update
the balance. So let's see what this looks like. So in our
fallback here, we'll say if the address of reentrant vulnerable
balance is greater than or equal to
one ether aka we're saying if
there's money left in the contract, then rancher
invulnerable dot withdraw. And then we'll put to get balanced
function in our attacking contract. We're going to attack
reentrant vulnerable by calling withdraw. When we get to this
send section, what are we going to do, we're going to have our
fallback function trigger calling withdraw again. Now when
we call withdraw again, bounces a message that sender hasn't
been zeroed out yet. So the contract code will go, oh,
you
still have some money here. Let's go ahead and let's send
you that, which will then again, trigger us to call withdraw. And
so we'll just keep calling withdraw until we're done. So
let's see what this looks like. So we compile this, and then
let's go to deploy, first, let's deploy the reentrant vulnerable
contract. Alright, and we can have any contract address, you
know, like the one that deployed it, we can have a deposit, let's
do, we have a deposit one ether deposit. Now we can check the
balances of it. Copy. Paste great, is one. So now let's have
a do 10. Deposit, it took the balance. And now we have this
much in here. So we have this much in here. And if we
withdrew, we withdraw all of it. And if we switched accounts, to
somebody else, we hit withdrew. Nothing would happen because
that other account doesn't have anything, which makes sense. So
there's a lot of money in here, right. And if we do get balanced
with the contract, we can see how much money it has, right? It
has thi
s much money total. Now what we can do on a different
account, let's choose this, this brand new account, let's go
ahead and deploy the attack contract. And we'll pass it the
reentrant value address as an input parameter. So we'll deploy
that. And now what we'll do is we'll call attack. And you'll
see even though this contract doesn't have anything deposited
in the reentrant vulnerable contract, we will still steal
all the funds in here or just about all the funds. So right
now hit get balance,
and a reentrant vulnerable, here's
what it is, get balance a here zero, you know, there's the
address, we had attack now, now that get bounce, oh, excuse me
in public, and withdrawal should be payable as well. Now we'll
pass one ether as an input parameter to our attack
function, and we're going to deposit just one ether. And then
we're going to withdraw. And we're going to keep withdrawing,
because our fallback function is going to keep calling withdraw.
And all we had to do was deposit one eth
er, and we're gonna be
able to pull out all 11 that are in here. So we'll hit attack now.
Transaction went through, the new balance of our contract is
12, because the one that we deposited and then the 11 that
we stole, and the new balance of our old contract is now zero. So
this is known as a reentrant. See attack. Basically, since we
call a function in another contract in the middle of our
withdraw, we allow code to run on a different contract. And the
code that ran runs on this contract, reca
lls withdraw
before balances is set to zero, we get to here we call the
fallback function of our other code, and it calls withdraw, and
we need to reread withdraw before we get to setting
balances a message that sender equals zero. So this is an
issue, obviously. And there are two ways we can prevent it.
There's the easy way. And then the mutex way, I don't wanna say
the hard way, it's just a different way. So one of the
things you'll always see in security tools is you always
want to call any e
xternal contract as the last step in
your function, or the last step in transaction. And we want to
update bounces to zero before we call that external contract,
because of balances of message sender is reset to zero before
we call external code, then if it were to try to re enter this,
it would hit this require step and just cancel out right here
and wouldn't be able to send any ether again. So that's the first
step that we can do. The next step that we can do is using
something called a mutex
lock. And this is what open Zeppelin
does with one of the modifiers that they have, we can have some
type of a Boolean called locked or something and just right at
the top, we can just say require not locked. Otherwise, revert.
And then the first thing we do in this contract is we can say
locked equals true. And then the last thing we do in here is we
say locked equals false. And using this lock in here, we only
allow one piece of code to ever execute in here at a time and we
only unlock it once
the code finishes. Now open Zeplin comes
with a reentrancy guard which we can use on our code. And it has
a modifier non reentrant which does essentially what we were
talking about with our locks it creates a variable called status
and changes it to enter whenever a function has been entered. It
runs out code, and then changes it back to not entered when it's
finishes. And whenever any code runs, it just requires that it
is not entered. So if we wanted to use this on our code, we can
import at
open Zeppelin, slash, contracts, slash security, slash
reentrancy. Guard about so we can inherit the functions by
saying NFT marketplace is reentrancy guard. And then any
function that we're nervous is going to have this reentrant see
issue, like maybe by item, for example, we would just add the
modifier non reentrant. Just like that. And that'll add that
mutex, that locking mechanism that we talked about. Now, the
mutex way is a little bit more explicit with our security,
right? Because we're s
aying, Hey, this is locked. This is a
non reentrant function. Still a best practice, whenever you call
external code, like what we see here is you do all of your state
changes before you call an external contract. Now, you
might be saying, Oh, that's cool at all. But what about how does
this relate to our NF Ts? Well, imagine for a second, instead of
message that sender dot call, this is, you know, all success
equals, you know, some NF T dot transfer from, and then we do
some transfers from stuf
f in here. And instead of doing some
fallback stuff, our NF T has our NF T's function transfer from
the similis code to re enter into our withdraw. If we have
our withdrawal set up like this, since we're still calling an
external contract with NFT. To transfer from that transfer from
in that external contract could be malicious and try to re enter
our contract. As a best practice, you always want to
change your state, before you call any external contracts that
you might not have control of, I h
ighly recommend playing around
with this a little bit just because seeing is believing. And
with that being said, again, all the code for this is going to be
available in the GitHub sociated with this lesson for this
reentrant vulnerable code. And with that, let's go back to our
NFT project. Okay, so now we know why we're
doing this safe transfer from at the bottom of our function here
at the bottom of our bio, because if our safe transfer
function from was a little bit higher, maybe what ends u
p
happening is we send multiple NF T's to the wrong address before
we update them. So that's why we do that. And we favor push over
Paul. As we said, Here, there's a whole lot of these security
tips that you'll learn going on through this course and in
solidity. But this is still fantastic, right, we have our by
item. And we have our list item functions. Let's do a cancel
item now, or cancelled listing. So we'll do a function. Cancel listing we'll do the NFT
address. And the UN 256 token ID. Thi
s will be an external
function. One will want to make sure only the owner of this
entity can cancel it. So we'll say is owner. And if T address, token ID
message dot Sen. Want to make sure that the NFT is actually
listed. So we'll do is listed and ft address, token ID. And
great. Now to cancel this, all we're going to do is we're going
to delete s listings NFT address token ID, we're just going to
delete that mapping. And then we'll emit an event item
cancelled message dot sender and if T addres
s and token ID. And
of course, we're going to create a new event here. We'll say
event item canceled. And it will be an address indexed seller
address. Index NFT address. You want to 56 indexed token ID. All
right. Great. That was pretty quick. Cancel listening. Boom.
Jack. Done. What's next? Okay, let's update our listings. So
we'll do function update listing address and if the address you
went to for the sixth token ID you went to the six new price
will update the price of this of this externa
l we'll make sure
it's listed with is listed say is owner do a token ID and
then we'll do message dot sender. Now to update our
listing We'll just say s underscore listings of NF T
address. At token ID dot price equals the new price that we're
giving it. And then we'll admit, we can omit like item updated.
But we can also just omit an item listed with MSG dot sender
NFT address, token ID, new price. Because essentially, by
updating it, we're essentially just relisting it with a new
price. So we'
re just going to do an item listed event, we only
have one more function to do, we need to do a withdraw proceeds.
So we'll say function, withdraw proceeds to get all the payments
for all of our entities. So we'll get the the proceeds by
doing new activity six proceeds equals s underscore proceeds of
MSG dot sender, right, we're getting all the payments that
were collected in by item, and we're saying if proceeds is less
than or equal to zero, then we're going to revert with no
proceeds. And we'
re going to make this a NFT. Marketplace
underscore underscore no proceeds. Excel create at the
top error empty marketplace no proceeds being otherwise, we'll
say s underscore proceeds of MSG dot sender equals zero. So we're
going to reset the proceeds to zero, right, we're going to do
this before we send any proceeds. And then we're going
to do our traditional way we send payments, so bool success equals payable, message dot
sender call value is going to be proceeds all blank here. And
then we
could do require, you know, we could do require
success, or we could say if not success, reverts revert
with Aleksey marketplace, transfer failed. And then we'll
make this a new air, air NFT marketplace transfer failed. Put
a semicolon here. And we're looking pretty good. Now we even
have a way to withdraw. So we have our five functions here.
Awesome. Let's just create a couple of getters. So maybe
we'll do we even copy this. Paste it here we'll say getter
functions like so. And we'll do functio
n maybe we'll do get
listing. Take an address, NF T address, the utilities X token
ID, external view, which returns a listing in memory. And we'll
say return s underscore listings of NF T address token Id like
so. And also function, get proceeds of address seller,
external view returns, you went to the desex return s underscore
proceeds of the seller. So we'll get how much money somebody is
owed. And then any listings and let's run a little compile here,
yarn, or hit Compile or hh compile just t
o see where we
messed up. Oh, we did a mess up. Wow, that's great. And now,
guess what? You have successfully created a
minimalistic NFT marketplace that's completely decentralized.
That is pretty wild. And that is incredibly powerful. And you
should feel really excited for yourself. Very cool, very good
job. But you know, we're not done, we got to write some
deploys and some tests. So let's jump into that. Now since we've
done this a couple of times, I actually encourage you to pause
the video
here and try writing your own deploy scripts and your
own tests. And then go ahead and come back and follow along with
us and see if you did it correctly, we're going to create
a new folder called deploy. Of course, we already have the hard
hat deploy in our hard hat config. So we know we're good to
go here. So let's go ahead and create a one. Deploy NFT
marketplace.js. Now once again, you've seen a lot of this
before, so we're going to spare the details do const network
equals require hard hat
to constant development chains,
equal equals require dot dot slash help our hard hats config,
which we should have let's see. Do we copy paste it over? No.
Okay, so we didn't copy paste over Are helper Hardhead config
from the last project, let's go ahead and grab it. Or we can
grab it from the smart contract lottery and paste that in here.
And we really only need this file for the development chains
here. Right for hard hat and localhost, we're going to grab
the development chains from that. An
d then we'll also grab
const. Verify, equals require, get this from utils. Verify,
right? Do we have utils, we have verify. Fantastic. Now we'll do
module that exports equals async. an async function where
it's gonna take get named accounts and deployments. From
the hard hat input parameter, and then we're gonna do const.
Deploy comma log equals deployments. And then const,
Deployer equals await, get named accounts. Which, of course,
we're getting from our heart head dot config. We have named
ac
counts, we have a Deployer. And we have a player or whatever
you have in here from our last project. And now does our entity
marketplace have a constructor? Construct? Nope, no constructor.
So we know args is going to be blank. And then we can say const
NFT. Marketplace equals await, deploy. And if T marketplace,
say from Deployer, args is going to be args. Log will be true.
And then wait confirmations will be network dot config dot wait
confirmations or one, two, this is going to be block on fo
r
patients, we'll go to the config, just make sure that
those are in here. I'm sorry, I didn't add them in here. So
we'll do block confirmations is going to be six for all of our networks. So I
actually grabbed this not from the last project, it looks like
I grabbed this from the hard hat starter kit. So I'm just going
to add those block confirmations in there. And now we're good to
go. Now we're gonna say if we're not on a development chain, not
development chains that includes network dot name
, and process
study and be done ether scan API key, then we're gonna go ahead,
we'll do log verifying. And then we'll do a weight verify. And if
the markets place dot address with arcs, and then we'll do
like log a whole bunch of hyphens here. And then finally,
module that exports dot tags equals all and then NFT Mar good
place. And we can test this deploy function with yarn
Hardhead deploy. Tada, we did it. Great. So now we have the
deploy function, we can verify we have our contract, what else
are we probably gonna need to do. Since this is an NFT
marketplace, we're probably going to need some NF Ts. So
what we can do is in our contracts, and we'll create a
new folder for tests got a new file in here called Basic NF T
dot soul. And in here, we can add that basic NF T from our
last project. Or you can just go to my or you can just go to the
GitHub repo associated with this course, go to contracts, test,
basic NF t, and then just copy paste. That works too. So this
basic NF t that we'r
e using is just pointing to the pug as the
basic NF T for us to use just to test this out. So now that we
have a basic NFT we're gonna need to create new file, oh to
deploy basic NF T dot j s, and we'll borrow a lot of the
boilerplate from over here. So we'll copy all of this actually
paste it in. We'll say const args equals blank, and we'll say
const basic NF t equals await deploy basic NF T. From
Deployer. args is going to be args. Log is going to be true
weight con confirmations is going to b
e network dot config
dot block confirmations or one and then we'll verify this with
if not develop meant chains dot includes network dot name and
process dot EMV that ether scan API key that will say log or To
find dot A dot await there if I basic NFT dot address, and arcs,
module dot exports dot tags equals all, and basic NFT. And
we can test both of these with yarn, art had deploy. And
fantastic. Both of these have been deployed. Again, you don't
have to pause. But it is a good way to really h
one in to really
sharpen those skills on doing all this. And repetition is the
mother of all skill. So repeating this stuff yourself
and thinking through these problems yourself and trying to
code these things yourself, are really what's going to make you
successful at this. All right, awesome. Now that we have our
deploy Betson, it's time to write some tests. Now, if you go
to the GitHub repo associated with this course, and you go to
the test folder, the tests and here are some of the robust,
we've actually written out of all of our projects, there's a
lot of tests in here. Now, pretty much everything in here,
we've already learned about and you already know how to do you
have the ability to do it. So I'm just going to go ahead and
get you started off, and we're going to write one test
together. And then I highly recommend you going back in, you
tried to write some tests yourself, to get that code
coverage to get that test coverage to be 100 percent. So let's go
ahead, we'll write on
e test together, then you should pause
this video and try to write some tests yourself. When you're done
writing tests and you think you've hit 100 percent, feel free to
compare back to the test that we wrote. So let's create a new
folder called tests. Test. And in here, we'll do a new one
called unit. And if you want to write staging tests later on,
you absolutely can we will not. We'll create a new file in here
called NFT marketplace.test.js. And we'll start some tests. So
we'll do const. cert
, expect equals require Chai
const. Network deployments, ethers, equals require hard hat
const development chains equals require dot dot slash dot dot
slash helper, hothead config. And we're gonna do the same
setup we've been doing. We'll say bang development chains dot
includes network dot name, question mark, describe dot
skip. Else describe. And if NF T marketplace tests, comma async
function, excuse me, this is just gonna be a function.
Scrabble oval is just a function, not an async function
,
like so. Great. Now let's get some variables and do a before
each. So we'll say NF T marketplace, basic NF T, we'll
create a constant price. So we're just always set the price
of all of our NF T's to the same thing. This will be ethers dot
utils dot parse ether 0.1. We'll say const token
ID for now will always be zero. And then we'll do before each
will be an async function. And we'll get out and we'll also
get Deployer. Say deployer equals await, get named
accounts. We're gonna need to grab g
etting into accounts from
hard hat as well wrap this all up dot deployer Excel and then
we'll also in our heart hat.config.js. Under get named
accounts, we also have something called player. Now I didn't talk
about this too much. But we're going to have a second account
which is defaulted to the first index, right? So we can do at
the top to come up player. And we can say player equals await
get named accounts dot player. Now we have a player and a
deployer account will do await deployments, tha
t fixture. All
will just deploy all of those contracts will run through
everything in our deploy folder. We'll get our NFT marketplace.
We'll say NFT marketplace equals await ethers dot get contract
and ft. marketplace. And then we'll do basic NF t equals await
ethers dot get contract. Basic NF T. The way ethers dot get
contract works is it defaults to grab thing, whatever account is
that account zero, which right now is our Deployer. If we want
to call a function on NFT marketplace, with the pl
ayer
being the one calling the function, we would have to say,
entity marketplace equals await and empty marketplace dot
connect player like this. And now whenever we call a function,
we would use the player instead of the Deployer. Sometimes what
I like to do and you'll see this in my code, is I'll do let NFT
marketplace contract. And then let NFT marketplace, and then
I'll do NFT marketplace contract equals await ethers dot
contract. And then I'll connect and set that to the NF T
marketplace.
Yes, we can do, we can automatically choose who to
connect by placing whoever want to connect to write and get
contract. But sometimes it's really nice to be kind of
explicit. So it's really up to you. I'm going to undo all that,
I just want to re show you that to make sure that you knew
that's how you kind of switch around with the different
accounts and the different users. Now that we have an NF T,
we're probably going to need to mint the NFT so that we can
actually place it on the market. So
we'll do await basic NF T dot
meant NF t, and then we'll approve to send it on to the
marketplace. So we'll do a wait. Basic NF T dot approve and T
market place dot address token ID, which is going to be zero.
And just like that, the NOC marketplace remember, it can't
call approve, because it doesn't own that NF t. So we need to
have the Deployer. Call approved, right. And remember,
since we're not we need to put basic NF T in here. Since we
didn't tell ethers who to connect this to it just
aut
omatically connected it to our Deployer because that's
what's at account zero. So it's the deployer calling minting it
and then the deployer approving to send it to the marketplace.
Only after this approved function has been called canned
the NFT marketplace called transfer from all those NF T's.
Now we're just going to do one test here. We're gonna say it
lists and can be bought. That's it, and this will be an async
function. And we're just gonna list the NFT and buy a weight
entity marketplace
.if We go to our energy marketplace, what are
we doing, we're listing it right, we want to list the item
or with the address token ID and the price. So we'll do dot list
item. Basic NF T dot address, token ID is zero, which we've
defined right here. And then price we've hard coded up here
as well. So we're listing it. So the deployer owns the NF T, the
player is now listing it, now we want to buy it, let's have the
player be the one to buy it. So what we're going to do is we're
going to have to
connect the player to the NFT marketplace.
So we can say const player connected NF T, marketplace
equals and ft marketplace dot Connect. Player. And then we can
buy the item by saying a weight player connected and ft
marketplace.by item. It'll be the basic and if we look back at
the end of the marketplace, what does buy item need, needs the
NFT address and the token ID. So basic NF t that address and then the token ID and after
this bot, we should check to see that the player actually does
indee
d own that NF team. So we can say const new owner and we
check to see if that owner is indeed updated. We can say basic
NFT dot owner of because NF T's have an owner of function, token
ID and then we also want to see that the deployer actually is
going to get paid. So we can say await and if T marketplace dot
get proceeds of Deployer. So now we
can do assert new owner dot two string equals player and we can
assert deployer proceeds.to string equals price.to string
because they should have been p
aid that price. And that's
right, it's actually a little bit easier. instead of grabbing
players from getting named accounts. It's a little bit
easier just to grab it right from ethers. So we'll do const
accounts equals await get signers. And then we'll save
player equals accounts of one just because when we connect
it's expecting it type of account and then the the get
named account is a different type. So Oh, it's just a little
bit easier to actually connect like this. So now we'll connect
to
the player like so just know that player and deployer are now
different types. So you'll see a little bit differences there. To
me, this is ethers that gets signers. And then when we buy
the item, we're of course gonna have to pass a value, it's gonna
be price, of course, we're going to need to pay the price of the
NFT. And then of course, this needs to be player dot address.
And that's the difference right now we got to do player that
address whenever we want the address of one of the ethers
ac
counts. And then this new owner, of course, should be in a
wait. And now we can run this all. In Tada, we see things
pass. So our NFT marketplace is able to facilitate the buying
and selling of an NF T with arbitrary humans. This is
fantastic. So we just ran the single test to show a little bit
of the oddities when working with NF T's and some different
accounts. But 100 percent If you feel up for the challenge, take this
time, pause this video and try to write some tests. Remember,
the goal her
e is for us to do yarn hardhat coverage, and see
what our coverage is and try to get it to be 100 percent coverage. If
we run it right now, we'll see Oh, my goodness, we are missing
a lot of coverage. Here, we have a ton of uncovered lines, on top
of uncovered functions, branches, statements, etc. Try
to write some tests to get this to 100 percent and then come back.
Okay, welcome back. Hopefully now you've written some tests.
And when you run your tests, you can get some of them, I look
like th
is, right. And these are my tests, these are the tests
that I wrote, you could do more you could do last. And let's
see, when I run yarn Hardhead coverage, I even missed some
lines. And I could I could test a little bit more. So make your
tests even better than the ones that I made. So these are the
tests from the GitHub repo associated with this. Now that
we've written some tests here, let's just write a couple of
scripts. And the reason we're gonna write a couple of scripts
is we're gonna need
these a little bit later. So we'll write
some scripts to mint, some amount of teased by some NF T's
etc. And we'll need this to fiddle around and play on the
front end a little bit later. So to create a script, again, we've
done this perform, let's do a script called mint, and list dot
j s. And this will be to mint at NFT. And then immediately listed
on the marketplace. So let's create an async function called
mint, and list. And down below, we're going to call mint and
list I'm going to copy p
aste with that same script thing that
we've been doing. Obviously, instead of Maine, though, we're
calling this mountain list. Now in this mountain list, where
it's a constant, NFT mark, and of tea market, place equals
await ethers dot get contract. And ft marketplace. And right
we're going to import ethers from hard hat. And then we'll do
we'll grab basic NF t. So we'll say const. Basic kind of T
equals await ethers dot get contract. Basic NF t. And then
first we'll mint a basic NF t. So we'll
do console dot log,
maintained at the top. And we'll do a weight or actual we'll say
const. Mint, TX equals await basic NF T dot mint NF t. And
then we'll do await min TX dot Wait, wait one block. And
actually we'll say const. Mint TX receipt. So that equals that.
And in this receipt, here's another reason why events are so
good. When we met this NFT. We're omitting the token ID in
an event in this document ID event. So we could say const
token ID equals mint TX receipt dot events of zero. That
args
dot token Id like that. And now we have the token ID. And now
that we have the token ID and the basic NFT. minted we can now
call on our NFT marketplace list item. So now we'll say console
dot log. Approving NFT right, it's gonna be real similar to
our tests here. I'm gonna say const approval TX equals await
basic NFT data prove NFT marketplace dot address
token ID and then we'll do a weight approved TX dot wait one
and we'll do console dot log listing NF t.at that and then
we'll do const T
X equals await and if T market market place
that list item and we'll do NFT marketplace that address So can
Id do await TX dot wait one, console dot log listed and cool.
And we can try this out by running yarn hardhat node, which
is going to run through our deploy scripts, right, it's
going to run to these deployed scripts here. And then in a new
terminal, we'll run our script, yarn hard hat, run scripts meant
and list dash dash network localhost. And we missed an
argument, oh, we need a price a
s well to list our entity. So
we'll create a constant price equals and we'll say ethers dot
utils dot parse, ether 0.1. And we'll pass the price and to the
list item. So oops, and sorry, it's not the marketplace that
we're listing, the basic NF t that we're listing, of course,
so run that again. And Bada bing, bada boom, got some listed
events. And we can see here, we're doing some listing and
awesome. So now we have a script. Alright, so now that we
have a script, and we're going to be writing
a couple other
scripts a little bit later, we essentially have a really solid
repo here are our totally decentralized, NFT marketplace,
this is absolutely massive, and you should be incredibly,
incredibly proud of yourself. Now, of course, this is all
code. And people can interact with this, if they're software
developers, which is great. But we're going to want to allow
anybody to be able to interact and list their own NF t's on our
marketplace. So what are we going to do? Well, we're going
to
want to build a front end for this. And now we're gonna get
into the second part of this lesson. So on lesson 15, we just
finished the backend. Now we're gonna move on to the front end,
we're gonna start with this morass code. The code for both
of these is pretty much nearly identical. But we're going to
start with Morales, and we're going to teach you how to do
both of these. And we're going to teach you the difference
between the Morales and the MoGraph. And kind of why we're
even using them i
n the first place, we're going to start with
Morales. So if you want to follow along with this next
section, all the code we're going to be working with, is
going to be in here. So you excited, I hope you are because
this is going to be a phenomenal session, we are about to build
one of the most sophisticated front ends that we can using the
tools that we have. And like I said, we showed you a little bit
earlier what this is going to look like. So let's do a quick
refresher here. So here's what
our front end is going to look
like. But what we can do is we can connect with our little
connect button, we hit Metamask Metamask pops up, we'll go ahead
and connect. And now that we're connected, we can see the
different NF T's in here. And if we're on an address that's owned
by us, it will say owned by you. And if we switch addresses, are
you I will go ahead and update, connect there. And now we're
owned by a different address. Now if it's owned by us, we get
this little hover that says Updat
e listing. And right now
it's worth 0.18. That's what it's listed for on our
marketplace. If it's owned by us, and we click it, we can
update it to a different price. Let's update it to 50 eath, or
whatever your layer one currency is, we'll just save new listing
price. We'll go ahead and confirm. And I'll say the
listing updated, please refresh, what we can do them. And we'll
mine some blocks on the back end, and boom, now we see that
it's worth 50 here. Now if we switch to a different account,
now we can see owned by Baba blah, and the hovered now says
by me enough is selected as a different user, I'm going to get
this transaction to actually buy it. Now go ahead and confirm
that I'm going to buy it, I get a little pop up. This is item
bought successfully. Now if I do a little refresh, we'll now see
that that NFT is gone from the marketplace since we bought it
right and it's no longer available to be sold. Now what
we can do then is we can come over to sell NF tees. And at the
bottom,
we'll see a withdraw proceeds. So whenever somebody
buys an NF T, the NFT marketplace actually keeps the
proceeds that actually keeps the result of the sale. So if we
switch back to our address that had the NFT listed, we can now
see Withdraw 50 proceeds because we know that we have 50 eath,
because we just bought that for 50. So if we hit withdraw,
Metamask is going to pop up, we can go ahead and confirm, wait a
little bit as transaction populates, and boom once it goes
through, and we'll see
now we have zero proceeds. Right? We
withdrew everything from here. So what we can do now is we can
relist that NFT. So if we come back, go back to the one who
just bought that at a T if we know the address and the token
ID of the NF t and we own it, we can go ahead and relisted you're
gonna place the address in here with the token Id give it some
sort of price. We'll submit we'll approve giving the NFT
marketplace access to our or NFT, to our little doggie. And
then we'll go ahead and actually
send the transaction to actually
list the NFT on a marketplace that we get NF T listed
successfully. After we move some blocks in the backend, we can go
back to the front end. And we now see, it's owned by us,
instead of the original owner, right and set for 10 ether. And
then we can of course, switch back to a different user, and we
can have them actually buy. Alright, so
now that we have the contracts, we know what this looks like on
the contract side. So now let's figure out how to do this on
the
front end side. So let's jump into our code editor. And begin
if we're on our hard hat and my NFT marketplace Free Code Camp
folder. That's great. But we're going to create another folder,
we're going to CD down and directory. And we're going to
make a new directory. I'm going to call it next Jas and if T
marketplace dash FCC. Now you can do next Jas marketplace dash
mirallas FCC if you want. Again, we're starting with mirallas CD
next Jas NFT marketplace FCC. So now that we have this folde
r,
will do code dot will open up a new VS code, or you can do File
Open folder and open this new folder. And we can begin working
in this new folder in here. Now that we're in our new project,
we're in our new folder, we're gonna do exactly what we've done
before. Yarn create next app, period. Okay, we've done our
setup here. Now, I don't like es lint. So once again, I'm just
going to go ahead and delete that. And what we're going to
add in instead is our prettier stuff. So prettier, ignore
pret
tier RC. Again, some people may strongly disagree with me on
that, but to each their own right, this is what I like to
do. So this is what I'm going to do. Now we have a minimalistic
react project, right? If we run yarn Dev, we open up our UI on
that site, copy this, or Command, click it. Tada. Welcome
to next. Jas Yeah, we've got an x js application. As we know, we
go to pages, we go to index.js. Let's delete everything in here.
Bom will leave the stuff and head if it comes with stuff and
head
will write Hi, exclamation mark. We'll save we'll come
back. And now we see Hi. And I'll zoom in a whole bunch.
Boom. So now we have some minimalistic reacts minimalistic
next, Jas. Now, I know we already started the project
here. But let's jump to the readme that's given to us. And
let's talk about how we want to do this what we want this to
actually look like. Well, we're going to want to make a
homepage. And in this homepage, we'll say we'll have it show
recently listed, NF T's homepage will
show recently listed
entities that will say if you own the NFT, you can update the
listing. If not, you can buy the listing. So we'll have that. And
then we'll have a sell page. And in this page, you can list your
NFT on the marketplace. So these are going to be our two main
pages, we're gonna have a homepage and a sell page. Now
we're going to have a ton of components, but we're really
only going to have two main pages. So if we go back over to
Pages, right, right now we have our apps dot j s,
which serves
our app, which is cool, which everything runs through. And
then we have our homepage. Let's also create right now, our sell
page or selling of T dot j s. And then in here, we'll just
make this really minimal, that we can copy most of what's in
here, we can actually just copy paste this whole thing, paste it
in here. And instead of high, we'll say sell page. We'll save
that. Now if we go to our localhost do debt slash sell.
And if T oops, we gotta run. Run the front end again. With y
arn
Dev. Sorry, we'll run yarn dev again. Now we refresh. And now
we can see sell page. So sell pages that slash sell page, and
then home is just going to be high. Okay, cool. So we have our
two pages. Which one should we work on first? Well, let's work
on our homepage. So we're going to be in our index.js. I'm going
to keep this front end bit running, we're going to hide it
oops, that's the opposite of hiding it, push it down, we're
going to hide it like that. And let's go ahead and let's start
building this. So we see in our index js, we have some head
stuff here, I'm going to change this to NFT. Marketplace.
Description is going to be just an empty marketplace. Like so Fabcon looks great. Now
if we do a little refresh, now it says NFT marketplace up at
the top here, which is good. That's what we want. Well in our
index page, what's one of the first things that we're always
going to need to do? You guessed it, we're going to need a little
connect button right? We're going to need our
users to be
able to connect to to web three to connect to a blockchain. So
same as we've done before. Let's go ahead let's create a
components folder. And we'll create a header component
component Nance folder and we'll create a new file The header
digests. Now remember, since we've done this before, with our
front end lottery code, we can always refer back to the lottery
code as well when we're building this, okay. And of course, we
have all of the code for this on the GitHub repo. So you can
use
that to what I'm not going to have you all do is last time, we
did that manual header thing, right, where we had to do all
the local storage and do all that crazy stuff, we're not
going to do that. This time, we're going to just do it the
easy way, we're going to just use the web three UI kit. So to
use this connect button, we're going to do yarn, add. And we're
not going to do dash dash Dev, because this connect button is a
necessary component for the front end, yarn add web through
UI Kit.
This also means we're going to do Morales and react
mirallas. I said, and this is where it might be a little
confusing. I know I said in here that we have both a Morales and
other graph edition. So we're still going to use the Morales
package in both of them. The only difference is we're going
to use a Morales server as well in our Morales edition. And
we're not going to use a morale server on our the graph edition,
they're both can use the Morales package because all the open
source hooks and
tools are still incredibly powerful, even if we
don't use the Morales server. So we're still going to use the
Morales package even when we're going to be using the graph.
Great. So now that we've added those all, we're going to do
exactly what we did before on our last next Jas process. So in
order to use our web through UI component in our app, dot j, s,
and do import mirallas provider quotes and without sorry, and
curly brace, it's from react mirallas like that. And then
we're going to wrap ou
r whole component thing in a morass
provider. So we're gonna do return, little open parentheses,
close parentheses here. We're gonna do rounds provider. And
then we're gonna do in if she allies on Mount is going to
equal false, because we're not going to use the server yet, or
else provider. Okay, cool. Now that we've wrapped our app in a
Morales provider, and go back to our header, we're gonna say
Export default function header, we're going to grab our Connect
button from what through your eye
kit. So we'll do import
connect button from web three UI kits. And then in here, we're
just gonna say return. The next button. Now what we can do back
in our app, J. S, is we can do import, import, do header from
dot dot slash components header, and we have our header, we'll
just put our header right above the component. And we're going
to add some stuff to the header in a bit. Let's just make sure
that we're importing the header correctly. Let's go back to our
UI here. And okay, boom, we have o
ur connects button. If we click
it, you know, we'll get this little pop up. And I'm way
zoomed in. So I'm going to anway zoom in. Now, what else do we want to put
in our header? Well, we're probably going to want to like
give this like a name and make this look a little bit nicer,
probably going to want to a link as well to our cell NF T page.
So let's create a navbar. So instead of just returning the
connect button, put this in parenthesis and we'll have a
return some other stuff too. So we can
use this nav tag, which
usually defines like a nav bar. So it's really similar to a div,
it's just another tag. Right, so now we'll put everything into
this nav tag. And in next Jas, we can actually make links using
the next Jas link tag. So what we can do in this is link allows
us to basically connect to different links or URLs in our
application, like so. So we can do import link from next slash
link. And in here, let's say if we want it to go to the
homepage, we can make a link. And we'll sa
y h ref equals slash
equals slash. And inside of this, we would wrap this in an a
tag to make it clickable. And then we could just say something
like NFT marketplace. Now if we save that, we go to our front
end, we now we see have a NFT marketplace button that we can
click and since we're already at home, we're not going to go
anywhere. But if we copy this link section, paste it below,
and we make another one for cell and f t. And we title this cell
NF t. Now we save we go back to our front end,
we now have NFT
marketplace and sell NFT if I click sell NF t we now get to
the sell page right we'll go back to the homepage sell page
flip back and forth. Awesome, very exciting. So now we have
have an incredibly minimalistic header obviously looks terrible.
So let's do just a little bit of formatting. And oftentimes
you'll do the formatting last. But while we're here, we might
as well do our formatting we're going to use what if you guessed
tailwind? You guessed correctly. So remember, tailw
ind with.
Next. Jas, you can always just follow along here. And we'll
grab, we'll do the exact same thing we did before we'll do
yarn, add dash dash Dev, that stuff right there. And then
we'll run a knit after these finished installing. So we'll do yarn,
and then paste that in. And there we go. So now we've got
our post CSS config, we've got our tailwind config. We're going
to grab tailwind.config.js. Paste it in here. And then we're
going to grab Global's dot css and open up Global's dot css,
p
aste that in there, and cool. Now we have tailwind in here.
Now that we have tailwind, we can do some tailwind stuff to
our header here. Let's let's create a div for all of these
for everything here, we'll create a little div for all the
stuff here, we'll make like a big section for almost like a
big sign saying, Hey, you're at the NFC marketplace, h1, which
stands for header one, and we'll give it a class name of padding
y of form. Padding x of four, we'll do font bold text will be
three XL, an
d then it'll just say NFT marketplace. Now we have
this NFT marketplace, which is nice bolt. Awesome. If you're on
your server you're going to kill it's going to kill it with Ctrl
C, and then we're going to restart it. And that's going to
pull in all the tailwind stuff. And now if we refresh our local
main, we should now see okay, and if T and F c, r gets place,
right, we now see this and big and bold, which looks a lot
better. So let's keep going. Let's give our whole nav a class
name. Equals w
e'll give it padding of five, border bottom
to flex flex row justify just if between n items center. We'll
see how that looks. Haha, looks a lot better. We're now kind of
like setting this up with a bottom border kind of stick and
some stuff like this, that looks much much nicer already. Let's
go down here. Let's make our buttons have a class name equals
lax lax row items center. And like I said, this is not a
styling class. So we're not really going to go over exactly
how we're styling this. An
d that is okay. But that's going to
move that over make that look a little nicer. We'll give our
link here, a class name equals Mr. For P six. And we'll give
both of these the same class name both these links, give them
some padding, so they moved away from each other some margin to
the right, so they're away from each other. And oh, I forgot to
do this Morales off equals false. We need Morales auth
equals false so that we don't automatically connect to a
Morales database or try to connect to Mo
rales database,
when we connect, we want to just connect with our Metamask. And
we'll change this to home instead of the marketplace. But
otherwise, that looks pretty good home selling of T Connect
button. And we can adjust the formatting of this to make it
look a little different. But I think for the most part, this
looks much better, right? Alright, cool, much, much
better looking header. Here, we have our app.js setup with the
mouse provider headers components. Let's now move on to
our index.
Let's now move on to showing these NF T's showing all
the NF T's in our marketplace. And here's where it's going to
already start to heat up and get really interesting. And actually
one more thing, we're going to grab this headpiece in the index
if you haven't. And we're just going to have it be in the app
JSX. So yeah, and our app.js we're going to put that header
up at the top, and just put a little, little div, Div. div,
like so wrapping around this whole thing. This goes here. And
this way,
no matter what page we're on, we're always going to
have this as our header. And we don't have to define it each one
of our little our things here. So we'll do a refresh. And it
says head is not defined. Sorry, that's because we're going to
need to copy import head from next slash head. Paste it into
our app that Jas import head from next head. And now we can
see we're going to empty marketplace no matter what page
we're on, because we're defining it at our app level. We have the
header in here
, we have this stuff in here, index almost has
nothing in it now. Let's do this. So what do we want to do?
We want the homepage aka our index to show recently listed
NFT. So the question is, how do we show the recently listed NF
Ts. How do we do that? Well, let's go back to our contract.
We go back to our hard hat NFT marketplace So we're looking at
at the marketplace, what do we have in here? How do we actually
see where NFT is stored? Well, they're stored in this listings
mapping. However, how
do we see all of the listings that are in
here? Well, this is a mapping, which means we have every single
address on the planet in here, we can't loop through the
mapping, we'd have to loop through every single address on
the planet, which is some insanely large number that you
and I could never fathom how many addresses there are. So
what are some solutions that we can take to this problem, right?
Because we're obviously not going to loop through
everything. So what do we do? What's what's kin
d of the first
approach, one of the first approaches would be like,
alright, Patrick, well, why don't we just create an array,
an array of listings instead, and this might be a good
approach. But what if then later on, we also want to get some
other weird data, maybe we want to get all the NF t's a user
owns NF t's a user owns, there's no array of NF T's that a user
owns. Again, that's just a mapping. But what if we want to
query some other weird data, or query some other weird data, or
what if
an array will be very gas expensive, which it is, if
we make this an array to loop through, it'll be incredibly gas
expensive. So we don't want to have to go back and change. So
I'm going to I'm going to type this out. Because this is
important. We don't want to change our protocol for just the
website, we don't want to change our protocol for just the
website, or we don't want to much change our protocol for the
website. Because if we were to make this an array, it would
become incredibly gas i
nefficient. And it would become
much harder to use this NFT marketplace because it would be
so much more expensive. And as you build more and more complex
protocols, you're going to realize that having an array for
every single mapping you have isn't feasible. This is one of
the reasons where these events come into play. So every single
time we list an NF t, we call this list item function. And we
omit item listed, this item listed event is stored in a data
structure that's still on chain, but j
ust smart contracts can
access it. However, guess what can't access it off chain
services can access these events. So what we do in this
case, is what we're going to do is we will index the events off
chain and then read from our database. So what we're
literally going to do is we're going to set up a server to
listen for those events to be fired, fired. And we will add
them to a database to query. So yes, we're literally going to
take every single time an item is listed, we're going to index
it
in a database for ourself. And then we're going to call our
centralized database to start and we're going to call that
database to do that. Now the question then becomes Whoa,
isn't that centralized? Hey, Patrick, we're talking isn't
that centralized? What the Hickety heck, and the answer to
that is, it's not necessarily. So the graph is a protocol that
does exactly this. It's a protocol that indexes events off
chain, and sticks them into this the Graph Protocol. And it does
it in a decentraliz
ed way, Morales, the way we're going to
show you first does it in a centralized way, Morales is
going to do it in a centralized way, which might be the route
that you want to go for speed for extra bells and whistles, so
that you can do local development, which is what we're
going to be focusing on here, or any of the other functionality
that Morales comes with. Because Morales does a lot more than
just that. That's something to keep in mind too, is even though
we are adding a centralized compon
ent, or logic, our smart
contracts, the real bulk of this application is decentralized.
And you can verify all your interactions are working with
this decentralized smart contract, we've actually been
using a lot of protocols that are centralized, like ether
scan, like open see, and some of these centralized protocols are
really important to this space. So we're showing you Morales to
get you familiar with working with one of these centralized
servers, in case you optionally want to make an appl
ication that
provides a centralized service. And there's a ton of tools in
the space like opens up and defender tenderly and more, that
are centralized, but give us massive, massive benefits. We as
a community are bringing more and more things to being
decentralized. And sometimes we need some training wheels to get
there. And then the graph is going to be the decentralized
way, which is a bit of a longer process to go main net, but
we'll explain all that when we get there. Let's learn how we
ca
n list the most recently listed NF T's and Morales in the
graph. Both have some really solid videos, I'm going to leave
some links in the GitHub repo associated with this. So if you
want to learn more, you should definitely watch both of those
because they are absolutely fantastic and will help you
understand this event stuff better. So normally, when we
read from the blockchain, we do something like contract dot get,
get listing, you know, and then we put it whatever our input
parameters are. C
ontract double blah, so instead, so we're going
to read from a database that houses All the mappings and an
easier to read data structure. Both Morales and the graph do
this. We've been using the morass,
open source packages and tools. However, Morales also comes
optionally with a server back end to give your web three
applications more functionality. However, there's a ton of stuff
that we're not going to cover that Morales can do to help
build your web three applications. So instead of me
cont
inuing to talk about Morales, and what it can do, we
have Ivan here to give a brief overview of some of the other
things that Morales can do take it away, and my name is Ivan, I'm from
rallis. And I'm here to tell you how you can speed up your
development by 10 times and I'm not over exaggerating, when
you're building something, you want to ensure that is scalable,
because your DAP may go global, it may get viral, it may go
mainstream, it can happen. And if it happens, you don't want to
start fr
om scratch, you want to use tools and services that
allow you to go fast, and also to go big. And that's exactly
what mirallas provides. At mirallas. We create tools, we
create infrastructure for developers in a way that you
have a single workflow, and they will soon explain what it means
because this is what saves you time. If you have a single
workflow for doing things. And workflow in web three really
means that you have to have a smart contract, whether it's a
token, the game, some kind of s
taking some kind of
marketplace, some kind of defy, it will be on chain, but at the
same time, you have to connect it to your back end. Because
when something happens on chain, you need to monitor that. So you
can create web hooks, you can create email, you can create a
push notification, you can run some custom code, you can run
some calculation, you can save something to the database,
everything on chain at the end of the day needs to go into our
back end. And when something is in your back en
d, it needs to go
to the front end. So for example, you change the UI when
something happens on chain or you change the UI. If your user
receives a transfer there is above a specific threshold. Or
if your user has this NFC, you can allow them access into some
kind of chat or some kind of exclusive piece of content. So
at Morales, we provide you with a full stack suite of tools that
is used by over 100,000 developers, it's really becoming
one of the most adopted tech stacks in web three. And it a
ll
starts with Morales identity, which ensures that you get one
piece of code, you write one piece of code and you can log in
your users across different blockchains across different
wallets. And in your morale is dashboard, you will get the user
profile, and you will get a web session. So mirallas allows you
to manage identities because a user profile can have many
different wallets from many different chains connected to
it. And all of the transactions will be synced from that user,
all the re
al time transactions will be synced about that user.
And also, you have established web session between your front
end whether it is a game, whether it is a web website, we
ensure that you have secure authenticated web sessions, and
we provide you with session management. So in case you have
your own bike, and then you have mirallas session identity
management, you can invalidate sessions, you can log in users
and do all of that great, all of these great things with one line
of code. That's very
important. Number two is Morales real time
I already mentioned a bit of it. But basically when you have a
user, you know exactly what's going on in real time. You can
run custom code, whenever a user does a transaction, you can run
custom code or do a web hook or email or push notification
whenever a user interacts with a smart contracts or when a smart
contract simply emits an event. This can be an trade in an NFC
marketplace, this can be ERC 20 transfer, you can be very
flexible by setting fi
lters. So you can say only give me alerts
only give me web hooks when the user transfers more than 10
NFCs. Or when this token transfer is above $1,000, and so
on so forth. This is morale is real time very, very powerful
things. Next are mirallas SDKs. So whether you're building a
website, whether you're building a game where full integration
with game engines, whether you're building for some other
platform, we have extensive SDKs that are easy to use that allow
you to do all of this that allow
you to connect to Morales and do
this very, very easily. And if you go to our documentation,
which I highly recommend you to do, you go to Morales, Doc's dot
morales.io, if you go to Doc's dot, Marisa Yo, you will first
and foremost understand what mirallas is in depth. So you can
think of it kind of like Firebase, but for crypto,
basically, it's a managed backend, that you can connect
your front end. Also, you can connect it to your own back end
using no GS SDK, it's very, very easy. But what
I wanted to show
you here is cross platform. So for each thing we have, let's
say you want to get nfts for your user, we'll show you how to
do it in simple JavaScript, vanilla JavaScript, how to do it
and react, how to do it using a web request. Let's say that you
just want to use a raw web request. Let's say you're using
some kind of language that we don't have SDK for you still can
use Morales just that you have to call the raw HTTP request.
And we'll also show you how to do it in Unity using
C sharp in
Unity game engine. So we're very, very clear cross platform.
And we are cross chain. So this means for example, when you log
in the user, you can create the user profile where you have the
lambda address, let's say your user uses Solana then they can
easily connect Aetherium, they can easily connect Binus chain,
the end, we're gonna add more chain soon, they can easily
connect L Ron, to one user profile. And then you have all
kinds of different wallets, different chains, and you have
one single user profile one single user ID. This is, by the
way, how it will look like in your database. As you can see,
you're going to have a user user table right here, you're going
to have all of their accounts. So in this case, I only have
eath. But if I have Solana, if I have other types of blockchains,
it will all be right here. And this is a database that also has
all my transactions. This is a database where I can set up
different listen events or smart contracts. So for example, open
s
ee I can watch open see smart contracts or something else. And
it's very, very variable, because this is MongoDB. This is
MongoDB, you can run MongoDB queries, it's very, very
variable. So in that sense, mirallas gives gives you a nice,
nice dashboard with everything you need to know about your
users, their sessions, their permissions, and so on, so
forth. And of course, you can connect to your own back end
using the Node js SDK. So this is Morales SDKs. And finally,
when we're speaking about th
e workflow, the final thing is the
API's, which I also already showed you, but the API is that
you can do RAW requests from any programming language from any
kind of architecture. So using this workflow, you can easily
achieve anything you want very, very quickly. You really have to
try Morales it is it will change your life. I can explain here
all I want, I have limited time. But as you can already see, by
this presentation, you're very curious, as you already can see,
by this presentation, you
want to try this as you already feel
by watching me here, you are very, very excited. We have to
get your hands dirty. So go, number one, two dogs, authorize
the sale, and go here getting started connector SDK in vanilla
or react and go through all of this. See the magic for
yourself. And if you want practicalities, go to youtube
channel and go to morality Oh, slash projects, guys, you all in
the community. Using Morales, you will succeed using rallies,
you will achieve your goal. And you're go
ing to do sooner than
you expect yourself. You're gonna surprise yourself. But
don't let yourself down. Go to morales.io sign up, get started, you guys. Now that I've
explained all of that, what does this look like? Well, this is
where we're actually going to start using mirallas with its
server capabilities. And we're going to sign up for a server
here. And we're going to use Morales as our back end for our
application. So to get set up with Morales, we go to
morales.io. We can go ahead and sig
n up for free. We'll put our
email in, we'll create some password. Why are you here?
Other please specify Patrick's amazing hardhat video, you don't
have to write that. But if you want to write that you can. What
did you hear about Morales for the first time? Well, you all
heard about it on YouTube, because you heard it from me,
and then pick your roll, I'm going to be a developer, we'll
hit next, I don't want to subscribe. But I'm going to not
be a robot and create your account. And it even giv
es us a
little property or create your first server. So our back end is
going to use a server to do any stuff on the back. So we'll
create a server. And if we were going to do a main net or a
testament, we choose one of those. But for now we're going
to do a local dev chain server. And again, this is one of the
advantages of Morales is it allows us to work with our local
dev chain. For indexing events, we can actually index our events
from our local hard hat node, which is incredibly, incredibly
powerful here, so check your email, and we'll have an
activate my account thing, email, we'll hit activate your
account. And it'll bring us back here and we'll recreate and
we'll do local dev chain. Alright, so now we're going to
create a new local dev chain server. So we're going to call
this NFT marketplace, we're going to select the region,
whatever region you want, I'm in the eastern United States. So
I'm going to choose New York. But whatever location works for
you, we're gonna do local de
v chain, and we're going to do
eath, local dev chain. And again, if you're building for
polygon, if you're building for avalanche, if you're building
for Phantom, if you're building for any of these EVM compatible
chains, again, your eath local dev chain, it's going to work
exactly the same. So we're going to add instance now. And we're
going to create a new application here. So we're going
to close now we have the server here, and it says ganache, but
it's really hard hat. Don't worry about tha
t. Now that we
have our server up, we can go to the Morales documentation, what
we're looking for is events, we're looking to sync with
events. So we can even do a little search in here for
events. And we see smart contract events platform
Automatic Sync, and even tells us a little bit more about why
do we need to sync and watch smart contract events. So
basically, this server our database is going to be looking
for these events to be emitted. But before we can do that, we
need to hook up our ap
plication to our server and if you go to
the React Morales GitHub right at the top And you'll probably
see saw this before, when you have this Morales provider in
their docks, they actually pass an app ID and a server URL. And
this is how we can actually connect directly to our servers
on mirallas. So what we're gonna do is right, now we're gonna go
back to our app that Jas and originally we've been saying
initialize on Mount equals false. When we say this, we're
saying, Hey, we're not going to
use a morale server, we're just
going to use the open source morass tools that y'all provide.
Now, we actually do want to use their server, right, we do want
to use all these bells and whistles that Morales comes with
out of the box. So we're going to change that. So instead of
saying initialize on Mount equals false, we're just getting
ready with the app ID and the server URL. So we're going to
delete this. And just like it says, in the documentation,
we're gonna give it an app ID and a server
URL. So we're gonna
say app ID equals, and this is where as a string, we'll put our
app ID. So if we go back to our Morales database, we can go to
view details. And we see all this information in here. And we
can grab our application ID, we can copy it, paste it in here.
And then we'll want to grab our server URL, which is at the top.
So this is the URL of our unique custom morale server. So we'll
say server, URL equals and then paste that in there like that.
Now, if you've been following along
with these tutorials, you
might be thinking oh, we're we're kind of just hard coding
that stuff right in there like that, how that seems kind of
that seems kind of bad. Well, if that's your intuition, that is
fantastic. So instead, we're actually going to put these into
environment variables. So we're going to create a new file, a
dot env file. And this is where we're going to put all of our
environment variables. Now next, Jas comes with built in support
for environment variables, which allow y
ou to do the following
use dot env, dot local to load environment variables or those
environment variables to the browser by prefixing. It with
next underscore public. So there's a couple of different
environment variable paths we can use. We can do dot env, dot
local, we can do dot env, dot this dot that that the other
thing, we're just going to do dot env, to keep it simple here.
But in order for our front ends, to read environment variables
from our dot env file, we have to do next underscore
public
underscore, and next Jas will look into our dot env file for
variables that start with this and only stick these environment
variables into our application. If we were to just do like
Morales server equals blah, blah, blah, it has no idea what
this is because we need to do next underscore public
underscore. And if we do that, and we'll do a console dot log,
look here, process dot env. Next public morale server and we
actually need to kill it and restart it. And then we go back,
we do a l
ittle refresh here, it'll say Look here as a do bla
bla bla, because that's what is in the dot env file with that
next public. So we'll grab our app ID, we'll copy it. And then
our dot env will do next public app ID equals and we'll paste
that in there. We'll grab our server URL, we'll go back to
here. We'll do next public server URL equals, we'll paste
that like that. And now at the top, we'll say const. App ID
equals process dot E and V dot next, public app ID. And then
we'll say const. Server
URL equals process dot E and V dot
next public server URL. Now that we've had these variables, we'll
stick them in like this. So this is how we can connect
our application to our morale server. Now, of course, we
haven't done anything yet. But we're getting started, right?
This is how we're going to connect to it. Now that we've
signed in, well, I told you that our morale server was going to
be indexing our events. And if you go to this Dashboard button,
this is our entire database. Everything
in this browser tab
is what's in our database right now. And as you can see, right
now, there's not a whole lot of anything, if we had any events
data in here, it would be in here. So we need to tell our
server, hey, you need to start listening for events. So we can
show the most recently listed entities. So morale server, you
need to start listening, you need to create a database entry
for every single one of these item listed events. And whenever
somebody buys an item, right, whenever somebody
buys that
item, or cancels an item, you need to remove that from your
database. How do we start telling Morales to start
listening to our events? Well, first off, well, first off,
we're gonna need to connect it back to to our blockchain. And
then we're going to say, which contract which events and what
to do when it hears those events. So we need to connect it
and then we need to tell it what to do when it hears those
events. So how do we connect our mirallas server to our hard hat
blockchain?
And right now, we're not running one. But let's go
ahead and we'll start up our hard hat, our local host
blockchain. So in one terminal, we're running the front end and
another terminal will see the download directory. We'll cd
into our hard hat NFT marketplace dot Free Code Camp
or Free Code Camp, and we'll do yarn hard hat node. And if we've
done everything correctly, it'll deploy our NFT marketplace.
It'll deploy our Basic NFT and then it'll start local HTTP web
socket at blah, blah, blah. So
that's good. So now that we have
that Node running, what we can do is we can go to view details,
and go to dev chain proxy server. So this dev chain proxy
server is going to be how we actually tell Morales to listen
to our locally running hard hat node. Now, to do this, what
you're going to need to do is we're going to need to download
this what's called a reverse proxy. And I have a link to this
in the GitHub as well, depending on what computer you're running
on will tell you which one of thes
e we actually need to
download. And then there's some troubleshooting tips down here
if you ever get lost, and if you're really, really confused,
what we can do what Morales FRP to download, do a quick search
on this. We even come right to the documentation, connecting
ganache to Morales note for Mac users download FRP dot Darwin
dot bla bla bla for the nosh proxy server. So I'm on a Mac.
So I'm going to download this Darwin AMD 64. And for look at
the releases, that's the first one at the top,
Darwin AMD 64.
So this is the one that I'm going to go ahead and download. I'm gonna click it, I'm going to
download it. Once I have it downloaded, we're going to open
it up, and we're gonna get a folder and we're gonna get a
folder that looks like this. The main things that we need are
going to be f RP and FRP C dot ini. F RPC is going to be the
executable it's going to be what we're going to run to connect
our blockchain node to Morales, and F RPC dot ini is going to be
basically the config fi
le to do this. Now, again, this is one of
the sections where downloading this is going to be one of the
hardest steps here. So if you get lost, please ask questions
in the GitHub, please ask questions in the Morales forum,
there is a Morales forum as well, where you can ask a ton of
different questions. And please check out the troubleshooting as
well. But what I'm going to do is I'm going to create a new
folder in here new folder called F R P. And I'm doing it in here
just to make it a little e
asier. But you could really put this
wherever you want, and then always refer back to it. And
what I'm going to do is I'm going to take I'm going to copy
these two files, and place it into this FRP folder. So now I
have FRP C, and F RPC that ini if you click on the F RPC, it's
going to be like, hey, it's binary, you can't really look at
this, don't click that, it'll just be a whole bunch of
nonsense, but the F RPC dot ini looks like a pretty typical
config file. And this is what we're going to a
djust. If we
even go back to our morale server, it'll give you what you
need down here. And we're using hard hat. So we're going to copy
everything here. We're gonna go back to our F RPC dot ini, and
then just paste whatever is in there in here. And that's how
we're going to tell this F RPC thing that we need to connect. I
haven't tried this out for users using WsL. So if you're using
WsL, let us know in the full blockchain solidity course, Jas,
make a new discussion, if you haven't seen it alre
ady, saying,
Hey, I'm using WsL for the F RPC. And here's what you need to
use. And then at the bottom, it says, run and enjoy. I'm running
on a Mac OS, which runs Linux commands, so I can just copy
this, I'll create a new terminal. And what I'm going to
do is I'm going to cd into that FRP folder. And I'm going to
paste that thing that I just that I just copied from Ross. So
we're running that F RPC executable dash c, which is dash
config, F RPC dot ini. If I hit Enter, it's gonna say log into
s
erver success, get run ID, blah, blah, server, UDP port,
and then a whole bunch of other stuff. If you're seeing success
stuff here, that means you did it right. And you can hit CTRL C
to cancel because we're not going to keep running it. Now,
if you want to just run this, you absolutely can. But I'm
going to show you another way to do this. And this is using the
Morales admin CLI. So everything that we're doing here, all these
buttons that we're pressing, Morales actually comes with this
thing
called the Morales admin CLI or the command line
interface. So this is a way for us to connect and run all these
buttons and stuff that we're pressing right from our terminal
and right from our shell. So I'm going to show you a couple of
commands on how to work with the admin CLI. And we're going to be
working with a lot of admin CLI commands. But all we're going to
do is npm install dash g Morales, admin CLI or for us
yarn Global Add. So we're going to grab that will do yarn,
Global Add routes,
admin CLI like that. And now we should be
able to run Morales admin CLI and see a whole bunch of stuff
like that. And if you ran routes, admin CLI, we have all
this stuff. And one of the big ones, one of the important ones
that we're going to be working with is this connect local dev
chain. So running this F RPC dash c dash f RPC ini. That's
going to be the same as running this connect local dev chain.
Now what I like to do is jump into our package dot JSON. And
we'll create an additional scrip
t in here for us to just
run yarn, whatever the name is that we want, and just to do
that, and just to make it a lot easier for us to connect our
local dev chain so underneath lint, I'm going to do a calm
Ma'am, I'm going to create a new command. I'm gonna say Morales
sync. And we're going to run the Morales admin CLI version of
this f RPC dash c thing. So what we're going to do in here is
we're going to say mirallas admin CLI, connect local dev
chain, dash dash chain, hard hat, dash dash mirall
as. Capital
sub domain. This is where we're going to put
the subdomain of a morale server, which if we go to, we go
back to our routes, admin data servers, we can go back to
server details. So it's going to be not the HTTPS, it's just
going to be from here all the way to the.com. So not even the
port, we're going to grab that we're going to paste that there.
And then we're going to do space dash dash f RPC path is going to
be dot slash F RPC, slash FRP slash F RPC. Now if we save
this, and we ru
n it, it's not going to work though. So if I
run yarn with our new script, Morales sync, it's gonna say
specify Morales API key, it's going to give us this prompt.
And in our dashboard, we have our API key, which we can copy,
we can paste it, and then API secret, we can copy and then
paste it. And then we'll say starting connection to hard hat,
which is great. But that's really annoying. And I don't
want to have to do that. So we're going to Ctrl C, we're
going to kill that. And what we can do i
s we can go into our dot
env. And we can actually add those as environment variables
that Morales is expecting. So when we run this Morales admin
CLI, it'll check our dot env file for Morales API key, which
we can copy right here. And then Morales API secret, which we can
copy, and paste right here. Now, the reason that these aren't
capital and doing next public, these are not going to be part
of our front end piece. These are keys that we're using on the
back end to test and for our local dev c
hain connection. So
we don't need to do next public, we're just going to leave it
like this. But now if I hit up and run yarn Morales sync,
again, it's not going to prompt me this time, it's just gonna
say starting connection to hard hat. And if you see this, this
starting connection to hard hat bit, we can come back to our
servers, we'll go to dev chain proxy servers, we'll hit this
disconnected button and refresh. And if you see connected, you've
successfully connected our heart add node, whic
h is running here
to our Morales server, which is awesome. And in fact, if you sit
on your heart had no terminal, you'll see the actual RPC calls
to our blockchain here. And you'll see Morales is
consistently calling f block number to make sure it's up to
date with what it has. So how do we tell a morale server to start
listening for events? Well, there are two ways we can do
this. The first way is with the user interface. So we can go to
view details, we'll go to sync. And right now it says no
sync
services installed. So we'll hit Add a new sync. And we can see
sync and watch address and sync and watch contract events, we
can watch the address for transactions or we can watch
some address for any events. And we can manually add all our
information here, you can select the chain description, decide if
we want to optionally sync historical, we could put the
topic of the event, the ABI of the event, the address of the
event filter, and then a table name. Or we could do all this
programma
tically, which is what we're going to do, we'll create
a little script that we can run, tell our morale server to watch
for those scripts. And we'll see our database get upgraded to
listen for those events. So back in our code, we're going to
create a new file called add events that Jas now we have one
terminal that's running our front end one terminal that's
running our blockchain one terminal that syncing our
blockchain with morass. And now we're going to do another
terminal. For anything else
we want to do like run little
scripts become the Morales docks, and you click connect
with SDK, there's a ton of different ways we can actually
connect with the SDK, we've already learned how to connect
with react by using React Morales. Now we're going to
connect with no JS since we're going to run a little Morales
script. And here's like a little example of what it looks like in
the documentation. But I'm gonna go ahead, so I'm gonna say const
Morales, equals require oralis slash node. And we
're going to
import the node extension of the Morales package into our script
here, we're going to require dot env dot config, which means we're
going to need to install dot env yarn add dash dash dev dot env.
And now we have to tell our morale server all the same
information that we would need to tell it on the user
interface. So one of the first things that we're going to need
is the address of our contract. We're gonna need to say const
contract address equals and this is where we go oh, well
, how do
we how do we get that contract address? The easy way to do this
is we just go back where we're running the blockchain And we'd
grab where that NFC marketplace is deployed. And similar to our
smart contract lottery where we created an update front end
script, we're gonna do the exact same thing here. So back in our
heart hat, NFT marketplace, and if T code, we go to our deploy
script or deploy folder, we're gonna create a new file called
99 Dash update, front end dot j s, and we're gonna
create a
little bit of our deploy process that will automatically update
our front end. So we can just grab the network address from a
file that is programmatically created. So we're gonna do
module dot exports equals async function. And now we'll say if
process dot EMV that update front end, then console dot log,
updating front end so that in our dot EMV, we have update
front end equals true. And that will be how we decide whether or
not we actually want to update the front end. And then we'll
create a function called up date contract addresses, which we
will await. And this will update the contract addresses of our
front end. So let's make that function. Do async function
update contract addresses, make sure those are spelled the same.
So we'll say const NFT, market place equals await ethers dot
get contract. And then yes, we need to import const ethers
equals require hardhat, we'll grab the NFT market place. And
then we're going to want to write our files in here to
someplace in ou
r front end code for us, we're going to do in a
new folder constants. And we're going to create a new file in
here called network mapping dot JSON. And we'll have this just
be a JSON object which keeps track of all of our deployments.
So if we deploy something to rink V chain will keep a list
will keep a list of it will say NFT marketplace will keep a list
of all the addresses of the IoT marketplace, comma basic NF T,
right, and then I list of all those right now we don't have
anything deployed.
So we'll just have it be an empty JSON object.
Now back in our deploy script in the hard hat NFT marketplace
project, we're going to keep track of that location. So right
at the top, we're gonna say const. Front, and contracts file
equals, and we'll place where it is according to your file setup.
So if I do cd dot dot slash, next JS NFT, marketplace, free
code, Camp constants, network mapping dot JSON, this is where
mine is. So you're going to want to put it wherever your location
is in relatio
n to your heart at Mt marketplace Free Code Camp,
it's my front end contract file, it's going to be right here. Now
that we have all that we're going to get the chain ID. So
we're gonna say const, chain ID equals network dot config dot
chain, id.to string. And we're going to need to import network
from hard hat as well. And then we're going to want to read from
this network mapping file to see what's currently in there. So
we'll say const. Contract addresses equals and we're gonna
do a JSON dot
parse Fs dot read file sync, front, and front and
contracts. File comma UTF, eight. Now here's what we're
gonna say, if chain ID is in contract. addresses, let's say
if this list of contract addresses doesn't include the
marketplace, then added on, we're gonna say if contract
addresses of chain ID of the NFT. Marketplace will say dot
includes NF T, market, Mar get place dot address, then so we'll
say contract address says chain ID NFT marketplace, dot push NFT
marketplace dot address else we're
gonna say contract address says of chain ID of NFT
marketplace, which is going to be a new entry now equals NFT.
Market market place dot address. So now we've updated our
contract addresses object and we just need to write it back to
the network mapping. So now we're gonna say Fs dot right
file sync front and contracts file comma JSON dot string five
contract addresses and then at the bottom we'll do module dot
exports dot tags equals and we'll say all or front end. Now
what we can do is we can
run just this update front end
script with yarn hardhat deploy dashed dash, network localhost.
And we only want to do this update front end script. So we
say dash dash tags front end, and we run this, and I ran into
an error FS is not defined, oh, I forgot to do const Fs equals
require Fs stride again, cannot set properties of undefined NFT
marketplace. Oops. And that's because this line is off instead
of this line, sorry. Basically, right now, what it's saying is,
it's saying, hey, this NFT mar
ketplace thing doesn't exist.
So we need to make it exists. So now we'll say contract addresses
of chain ID equals a new entry of NFT marketplace and adds its
first parameter, it's going to be NF T, Mark gets placed dot
address, like that. Now we can run it, and updating front end
looks like it's done. So if we go back to our front end, we now
see we have an entry for localhost with NFC marketplace
with the address in our network mapping dot JSON. So if you did
that correctly, you should get thi
s. If not, if you're having a
hard time with that you can, of course, just go ahead and hard
coded in but I do highly recommend you do it
programmatically, because your life is going to be a lot
better. So cool. We have this update front end script that
works now, so we can put this back off to the side. And let's
keep going. So we now have this network mapping file with
contract addresses based off of the chain ID. So what we can do
is we can pull that in as well, we'll say const contract,
addr
ess says equals require dot slash constants, slash network
mapping dot JSON. And now we can get the contract address based
off the chain ID. So we'll say chain ID equals process dot E
and V dot chain ID, or 31337. So in our data and V, we'll make a
new entry called Chain ID. And for now, we'll do 31337. And now
we can get the contract address by saying contract address
equals contract addresses at the chain ID of n, f t, Mark get
place of zero. So we're going to go into that network mapping, go
to the chain, Id go to the NFT marketplace and get the most
recently deployed NFT marketplace boom. So now we have
the contract address contract address says Excuse me. Now in
our add events, we'll create a new function kind of similar to
what we're doing, we'll do async function main. And this will be
our main function. And then of course, we're going to copy
paste that main script thing we've been doing this whole time
domain dot then catch blah, blah, blah, we go back to the
morass documentat
ion, though, we can see we're going to need to
grab our server URL, app ID master key, and then start it
up. So we're gonna do the exact same thing. So once again,
sorry, before we even get into our main, you can do it in your
main function, if you want. We'll say const server URL
equals, and we can just grab this once again, from our web.
So we'll say process web dot next public Morales server URL,
we'll get the app ID equals process Studien v dot next
public Morales app ID, and then we'll say
const, master key
equals process study v dot master key. So we don't have a
master key in here yet. So we'll create a new one called master
key. We'll go back to our Morales front end, we'll close
out of this, we'll hit View Details. And we'll grab that
master key. So we'll copy that, go back to our code editor and
paste it in. And now we have a master key in our web as well.
We don't want our master key on our front end. So we're not
going to put next public like that. Now, the first thing we'r
e
going to do in our main function is we're going to do await
Morales dot start server URL, app ID and master key as the
input parameters for this will do a little console dot log,
working with contract address, contract address. Now we're
gonna go ahead and add all those same pieces that we see on the
UI. So what are the events we want to listen for? Well, if we
go back to our code here, so we have our NFT marketplace, where
just type in event we have item listed item bought an item
cancelled.
So we have three events we want to listen and a
Morales, they have this add new events synced from code, which
we're basically going to be following. To do this, we need
to obviously start and then create our options for the
event. We have the chain address topic, abi elimite, table name
and sync historical and then we just do Morales dot Cloud dot
run, watch cloud event options use master key. And that's
pretty much it. So we're going to follow these documents here
to do our code. So let's star
t with item listed. Let's create
some options for our IDed listed event. So we'll say let item
listed options, or we could do const if we want to, but I'm
just gonna do let item listed options equals. And first we're
gonna need the chain ID, which we have, because we're getting
it from Morales. Now the first thing to point out about chain
ID is that ralis understands a local chain is 1337. So even if
you're on 31337, if you're doing a local development, you got to
switch it to 1337. So we're goi
ng to make another variable
called Morales chain ID. And we're just going to say, let
Morales chain ID equals chain ID equals 31337. Question mark
311337. Otherwise, chain ID but we're saying since Morales
understands that any local Dev is going to be 1337, we're going
to say if chain ID equals 31337. Then have Morales chain ID equal
1337. Otherwise have an equal whatever whatever our chain
ideas and in our dot env we can decide okay, if we want to do
rink B, localhost main net, etc. We're gonna
say Okay, chain ID
morass will do your Morales chain ID, comma, we'll say what
else do we need? We did the chain ID, we're gonna skip
description, a sync historical. So hit sync historical allows
the node to go back throughout the blockchain, grab all the
events ever emitted by that contract. Since this is a very
small local blockchain will just say sync historical is true.
Like that. Okay, what else do we need? Okay, we need the topic,
the topic is going to be your event information. So to get
the
topic, go back to our event code, and the topic is just
gonna be the name of the event, plus the type of the parameters.
So we're gonna go back to our code, we're gonna go back to
here, we're gonna say, topic is going to be item listed, and it
takes an address, an address, an address, you went to 56, and a
UNT 256, address address, you interviewed six, YouTube and
six. Those problems seem like that, we also need the API of
just the event, which again, we can find we go back to our hard
hat
project, we go to artifacts, we go to contracts. And if the
marketplace dot soul NFT marketplace dot JSON, our ABI
starting from here is going to be the ABI of the whole
contract. And we just want that item listed event. So we did
Ctrl F, and we found it here. And we're going to grab from
right after it says type event, we're going to copy we're going
to scroll up to write up to anonymous false, right, so this
bit describes the ABI of the event. So we have internal type,
address, name, seller ty
pe, address, and if T address token
ID price item listed, right, so this is going to be the ABI of
our just our item listed event, we can take that and we just
stick it in here, hit save, and mine auto format it to get rid
of the parentheses. Okay, what else do we need, we have the
topic, we have the ABI, we already have the address, we're
not going to do a filter. And then we need a table name. So
we're going to do a new line, we'll say table name, it's going
to be item listed. And this is goin
g to be the name of the
table that we update in our database. So we're gonna get a
new table in here called item listed, and it's just gonna be
filled with information about the item listed event. And
that's it right and we would hit confirm if we were doing this on
the UI. And since we're doing here, we'll just hit save, this
is one of our events, we want to do this for all of our events.
Let's do it now for item bought. So we'll say let's, item bought
options, equals and we'll repeat the proce
ss, some of the stuff
at the top is gonna be the same, the chain it is gonna be the
same sync historical is gonna be the same. So we can just grab
those two, paste them down here for item, but the topic is going
to be different. The topic is going to be item bot is the name
of the event, it's going to take an address, an address, a un 256
and a un 256. The ABI is going to be different. Once again,
we're gonna go to our Hardhead NFT marketplace, we'll look for
a bot, you'd find this event here. W
e'll copy this, go back,
we'll paste it in here, we now have item bot, we'll give it a
table name of item bot. And then one more then we have let item
cancelled options equals and we'll do chain ID that's going
to be rouse chain ID gonna be the same boilerplate from the
top address. Contract address topic is going to be different.
The topic for this it's called item cancelled, and it takes an
address, an address and a UNT 256 will say sync. Historical
will be true, historical is true. And then w
e need the ABI.
Once again we can go back to our hard hat, compile information we
can look for item cancelled, grab that ABI of that event.
Copy that. Come back to our running code pasted in. Oops and
I didn't give item canceled. Let's give item canceled the table name, which will be item
canceled. So now if I zoom out just a hair, I now have item
cancelled options, item bought options. And item listed options
are telling Ross Hey, listen for these events, whenever you hear
an item canceled even
t, stick all this stuff into a database.
Whenever you hear an item bought event, stick all this in a
database, whenever it emits an item listed, stick all this in
in a database so that we can read from it. So we're indexing
these events so that we can query them much easier. Now to
send them up to our our server will say const listed response
equals await or Alice or Alice dot Cloud dot run, watch
contract event will pass the item listed options. And then
one more comma, and then we pass an obje
ct in here where we're
just gonna say use master key is going to be true. And we'll do
the same thing we'll say const bot response or passing the bot
item options. So we'll say bot response equals await or else
dot Cloud dot run, watch contract, event, comma, item
bought options. Comma, use master key that's going to be
true. And then finally Kant's canceled response equals await
oralis dot Cloud dot run, watch contract event, comma, item
canceled options, comma use master key is going to be tru
e.
Now this Morales dot Cloud dot run API call to our server that
we're making is going to return a response. And let's look at
the docs to actually see what that response looks like if it
worked out. Well. In the terminal, you'll see success
true. So this is the return we're getting from the API. So
just to make sure everything goes well, I'll do an if listed.
response dot success, we're getting that success object from
the rails server, we'll just do a console dot log success
database updated
with watching events. And then else we'll say
console dot log, something went wrong with a duck. And of
course, we're not just looking for let's say response dot
success to be true. We also want canceled response that success
and bots response is successful. Then say hey, you
did it. Otherwise say hey, something went wrong. So this is
how we're going to programmatically tell our server
our database to listen for events. So we just do await
Murata Clodagh, run, watch contract events, we pass it t
his
object with all these parameters and flags in there. And then
that's it. And then we can send them is because I put next
public Morales server URL. And in my DMV, I just have next
public server URL. So let's change the name here. Looks like
our server URL was wrong. And our app ID name is also wrong.
So let's fix that next public app ID master key looks correct.
Okay, cool. So let's know things right? More Alice, like cada
run, Rasta cloud run, when we run this in our database, if we
hit ref
resh, right now, we don't see those tables in here. But
once we run this, add events.js, we should call our server and we
should tell it hey, you need to add these tables. And you need
to start listening for those events. So in a new terminal,
we're going to run this add events such as, so I'm going to
make the terminal nice big. And this is where if something goes
wrong, it can be a little frustrating to figure it out how
to fix this. So if you run into an issue here, if something's
not working
as expected, please use the GitHub repo associated
with this course. And also the Morales forum is here for you,
and Stack Exchange Etherium. So we're going to run Node, add
events, dot j s, and we'll hit enter. Okay, boom, now we see
success database updated with watching events. Now, if you ran
into an issue, and you rerun it, and it gets something went
wrong, there's a chance that it could still be correct, right,
because it returns false. It returns that there's an issue if
any of these alr
eady have the table in there. So if we go back
to our database here, and we hit refresh, I can now see item
bought, item canceled and item listed in my database. And
again, you can see them by hitting the drop down on your
server and hitting dashboard. We also see event Sync Status. And
this is how our database knows that it needs to be listening
for some events, and it's got all the information about how to
listen for our events in here. So cool. So now we are listening
for events. This is fant
astic. So now what this means is our
database is now listening To our blockchain node, and it's
listening for events in here it's listening for these item
listed item bought item, cancelled events. So let's go
ahead and test this back in our hardhat NFT marketplace Free
Code Camp window. We have some scripts in here. One of them is
mint and list. So we went to New NFT. And we listed on the
marketplace when we list an NF T, well, our mirallas database
should hear that item listed event and go ahe
ad and stick it
into this item listed table that it made. So for us to test this
out, let's open up our terminal in our Hardhead NFT marketplace
repo. And we'll run Minton list for our localhost before we
actually run it, just be sure that our hard hat node is synced
up with our Morales server in order for your database to
actually grab that event. Your local hard hat node needs to be
connected. So we'll do yarn, hard hat run scripts, mint and
list.js dash dash, network localhost. Let's enter. O
kay,
minting, approving listing listed now if we flip back to
our database, after a quick refresh, what do you know, we
see that there's an indeed an item listed events in our
database, we can see information about a tool we can see there's
a block hash, a timestamp, we see the token ID that was
listed, we see the price of the listing the transaction hash, we
see all this information about our event. And now it's in this
database for us to query. So if you have reached this point, you
have succe
ssfully set up an indexer with the Morales
database. And you should be super pumped because this is
really powerful. And now we're getting advanced, we're starting
to do some advanced stuff. So if you've made it this far, huge
congrats. This is already really cool. Now, some other
troubleshooting help here that I've run into many times myself,
let's say I've left this project, and I've killed my
heart hit note, I'm going to kill it right now. If I stopped,
my heart had node and I come back to my
Morales admin, I'm
going to view details dev chain proxy server, I'm now
disconnected. And if I hit this little refresh, I'm disconnected
of course, because I'm not running my heart headnote
anymore. If I restart my node, my note is now restarted. My
connects local dev chain command is still running. If I re fresh
it, it'll now say connected, which is great. However, if I go
back to my blockchain, or if I go back to my hard hat, NFC
marketplace script, I run yarn, hard hat script, mint and list
again, network localhost, I go back to my database now, and I
do a refresh, we don't see that item listed in here. So our
mirallas server is looking to make sure that that the
blockchain we're working with is the same one. So if we reset our
blockchain, like we did, right, we canceled it and we reset it,
our database is gonna get really confused. So what we have to do
is we have to hit reset local chain, reset local chain, we
want to make sure that our new local chain is running, and that
we're
connected here. So we'll hit reset local chain, and this
will tell them Ross, hey, we reset the chain, it's okay,
please continue doing so. And once we hit reset local chain,
we're not going to see that item listed in here. However, if we
go back, and we rerun mint and list network, local host with
this reset local chain. Now, if we go back to our Morales
database, we hit refresh, we now see that new one has gotten in
anytime you stopped your hard hat note, anytime you reset your
hard hat node,
the takeaway is you're going to need to go to
View Details dev chain proxy server and reset local chain.
Now you can do that programmatically as well, we're
not going to go over how to programmatically do that. But
that might be something you want to add to your hard hat deploy.
The other thing to note is that it didn't clear out our last
event, right, the last event. And if I go one minute and list
again, after completes, we'll have another event in here. Okay, this is great. So all of
this is
being said, the reason we're doing all this in the
first place is so that in our index.js we can start listening
for events. How do we show the recent listen entity. So now we
have a database of listed entities. So what we could do,
we could just query this item listed table right and grab
everything in here. However, we have an issue here, what happens
if someone buys an NFT, if someone buys an NF T, the item
listed event will still be in our database. But technically it
won't be on the market
place anymore. It'll be gone, it won't
be listed. So what can we do, there's a number of
architectural choices we can make to get around this problem
to solve this problem. But one of the things we can do is
actually we can use mirallas Cloud Functions. So Morales
cloud functions allow us to just really add anything we want our
front end to do from the morale server. And these are functions.
These are scripts that are going to run on a morale server
whenever we want them to. So we go to our serv
er hit the little
drop down and we hit Cloud Functions. Now this is where we
can write somewhere else stuff to run on our server whenever we
want. And we are going to set up our Cloud Functions in our IDE
by hitting this little drop done to actually sync up our Visual
Studio code with our Cloud Functions, we can just run this
command here, and it will add whatever cloud functions we have
in some cloud folder to here. So what we can do back in our VS
code, let's make a new folder, new folder call
ed Cloud
Functions. And in here, we'll create a new file called Update,
active items.js. So in here, if we were to write something like
console dot log, hi, we can actually have this automatically
saved on a morale server. And the way that we do this is by
running this command. Now, we want to make it so that it's a
lot easier for us to run this command than just always having
to run this massive thing. So what we're going to do is we're
going to open up our package json, and we're going to make
another Morales script here, right below here, we're going to
make another Morales script, we're gonna say Morales Morales
cloud, and we're going to have it, run this command. So we're
going to copy this command here, paste it into our package json.
So it's going to be Morales admin CLI, watch cloud folder,
we don't need the Morales API key, because it'll grab that
from our environment variables. We don't need them rouse secret,
because it'll grab that from our environment variables, we do
need
the morale subdomain autosave one. And then the
Morales cloud folder is going to be that new Cloud Functions bid
that we made that slash cloud functions, functions. Now, in a
new terminal, if I run yarn, more Alice cloud, which is going
to be same as running this huge function here, I hit enter,
it'll say compile, you know, version, blah, blah, compiling,
blah, blah, changes uploaded correctly. And if we go back to
our front end, we can see this console dot log ky and our front
end being update
d. And if we continue to run this in our
update active items at Jas, we could also write console dot
log, you'll save it. And if this is still running, it'll
automatically upload it. And now we can see if we do a little
refresh on our front end Cloud Functions, we can see it's been
uploaded here. Now at this point, if you have a ton of this
stuff running, you might see CPU 100 percent, you might see this little
thing pop up and the server might start going a little bit
slower, we're starting to
use a lot of network activity here. So
I'm going to close my yarn Morales cloud for now. And I'm
just going to upload it once when I need to. Because we're
connected, we have it listening to events, we're having it doing
more and more stuff here. And it can start to put a lot of load
onto the server. So we're just gonna go ahead and we're going
to cancel that out. And now the CPU is a lot lower. But if we go
back to Cloud Functions, we can see it's still in here. And
anytime we update our Cloud
Functions, it'll update our
server with those cloud functions. And we'll just run
that darn morass cloud once we're all done here. Anyways, so
right now we're trying to figure out, Okay, we have item listed,
but if someone buys an item, technically, it won't be listed
anymore. But our item listed table will still have it listed.
So what we can do is we can create a Cloud Function that
runs whenever we want. And like I said, we can have these run
whenever we want. We can call these whenever we wa
nt. But
we're going to create a Cloud Function that only runs.
Whenever one of these events are synced item listed item
cancelled or item bought, we're going to create a new table
called active item, an active item is going to say, okay,
anytime it's listed, it will be active, but when it's bought or
cancelled will remove it from the active item list. So we're
going to create a new table. So let's go ahead and do that. We
started off with more Alice thought. And then if you're IT
auditors that y
ou don't need this, we don't need to import
Morales here, because we're going to upload it as a Cloud
Function. And our server already just automatically injects
mirallas into our scripts. So we're gonna say Morales dot
cloud, that after save, and there's a whole bunch of stuff
you can do with your Morales cloud. And again, you can find
these all in the documentation. The after save keyword means
that anytime something gets saved on a table that we
specify, we'll do something. And it takes two p
arameters. So it
takes what table that we want to do something after it's saved.
And we're gonna say item listed. So we're saying anytime
something is saved to the item listed table, we'll run some
async function. And we'll put request in here.
Because anytime something gets saved, it comes with a request.
So anytime an item listed happens, we want to add it to
our active items list. And our requests come with this is
flagged called confirmed. So we'll say const confirmed
because every request,
every event actually gets triggered
twice. So once a transaction goes through, it triggers a save
and then once again, once that transaction is actually
confirmed, we actually only want to update our active item when
the transaction is actually confirmed. So we'll say const
confirmed equals request dot object dot get confirmed.
Screening, get the confirmed attribute from that request. And
then we're also going to make A logger will say const logger
equals oralis dot Cloud dot get logger. And you
'll see why in a
second, we can actually write logs to our Morales database
with this logs thing. So any logs we can add into here, and
I'll show you that in a minute. So console logger Morales dot
cloud, get logger. And then we'll just do logger dot info. Looking for confirmed x, and we
can actually test this right now. Right, we can actually test
this right now. In our logs. We should see looking for confirmed
TX Once an item listed and saved now to test this out just to
test that our logger i
s actually working. Let's run yarn, yarn
Ross cloud just update active items to our to our morale
server changes uploaded correctly. Okay, we'll kill it
now. And now in our where we have our Minton, lists script.
Let's run Minton list. And we should see on our server we
should get those logs. Now if we go to our server, we do a little
refresh here. And if we look at our logs now we can now see
looking for confirmed TX in our server logs. Now in our logs.
Here we see we only see that looking for
confirmed TX once
and I just told you, it actually triggers twice once when the
transaction is first sent. And then once when the transaction
is confirmed, aka has block confirmations. And additionally,
if we look in our database at the item listed, and we scroll
all the way to the right, we can see confirmed equals false. So
we only want to count this item listed event interactive items
when confirmed is true. So what we want to do actually is we
want to update our scripts to add one block conf
irmation on
top of our local Hardhead blockchain so that these can be
changed to confirmed now to get around this. What I usually will
do in my mentalist script is I'll add a new utility. So I'll
go to my utils, I'll do new file, and I'll create a move
blocks.js. And this will be a utility that I use to actually
move the blocks. So when we run our own heart hat node, we
actually have complete control over what we want our heart hat
node to do. So what we can do is we can actually manually mine
n
odes and actually move blocks ahead so that Morales knows Oh,
okay, this transaction is confirmed, right, because we're
mining the block with the transaction. And that's it, and
Ross is just going to forever be waiting for the next block. So
we want to add some functionality to our scripts,
where we just mine a block after it's done. Now, keep in mind
that if we mined like 1000 blocks or a ton of blocks really
quickly, Moorehouse might have a hard time indexing that. So we
really want to just mi
ned one at a time and give me enough time
to index each block that we mined. So we're actually going
to build a little script, we're going to manually mine using
this EVM mine RPC method that comes with our heart hat
blockchain. So we have this new move blocks script. And let's go
ahead and make this. So instead of this being our script, we're
going to have like a main function at the bottom, we're
just gonna have this be a utility that we're going to
import into other scripts. So we're not goin
g to need a main
function here, we're just going to need to make this an async
function. And we'll call it move blocks. And then we'll say
amount, which is going to be the number of blocks, we want to
move, we'll also put a sleep amount and default it to zero,
this sleep amount is going to be an optional parameter. If we
want to move blocks and sleep maybe a second between blocks to
resemble a real blockchain, we can have that in here too. So we
can have it resemble a real blockchain by sleeping
every
time a block is moved or just kind of waiting every time a
block has moved. So in our move block scripts, we'll do console
dot log, moving blocks, dot that dot, and we'll say for let index
equals zero, and we'll do a for loop around the amount and call
that EVM. Mine in this for loop index is less than amount index
plus plus a weight network. And then we got to import network
oops, we got to import network from hard hat here, await
network dot provider dot request. And then we're going to
request the method e v, mime, comma params are going to be
empty. And this is actually the same way we can make raw calls
to our blockchain nodes. We don't do a lot of this because
ethers abstract this under the hood, but we're making a raw
call to EVM mine. Obviously, you can't call EVM mine on a real
blockchain because you can't just tell a blockchain node to
mine the next block. Since this is our local hard hat node, we
can call the VM now we're gonna say if sleep amount is greater
than zero
, or just if sleep mount, then we're also going to
have this script sleep or wait a short duration. So up at the
top, we're actually going to create a new function called
sleep, which is going to input a time in milliseconds. And this
is going to return a new promise, right? Because
remember, in order for us to wait for some time we got to use
promises, which we've learned before. And this promise is
going to take a function with resolve as an input parameter.
And we're just going to say, set ti
meout is going to be resolve,
comma, time in Ms. So the way we can sleep in JavaScript is we
return a new promise. And we just call this set timeout
function, which basically just weights the time in
milliseconds. Now to actually sleep. We'll say console dot
log, sleeping for sleep amount. And then we'll do await, sleep,
sleep amount, and this is going to be in milliseconds. So since
sleep returns a promise, we can call it with await to say, okay,
wait for this sleep function to finish. And the
sleep function
is only going to finish when the time in MS in time in
milliseconds finishes. So now we have a function called move
blocks, which will actually mined blocks on our local
blockchain, so that Morales can get that block confirmation that
it's looking for now at the bottom, we'll just do module dot
exports, move blocks, move blocks, and then we'll also
export sleep as well, because why not? Equals like that. Now,
what we can do back in our Minton list, up at the top,
we'll say const,
move blocks equals require dot dot slash
utils slash move blocks. And then we'll also import network
from ethers network. And the down in our script. Just right
at the bottom, we'll just say if network dot config, that chain
ID equals equals 31337 await, move blocks, we'll say we'll
move to blocks and then we'll also do sleep amount equals
1000. We'll wait one millisecond between each block that we
mined. So sleep mount equals 1000, which is going to be one
millisecond. Now let's even just comme
nt all this out for a
second. We'll just run this script with only this live.
Right we'll pull this up the yarn hard hat, run scripts,
mentalist dash dash network, local host, we'll just move the
blocks move back to our front end, we'll refresh, we'll go
look at item listed. We'll scroll all the way to the right,
and now we see confirmed is true. And now if we were to look
in our logs, we would see that logging item happened twice.
Alright, so Let's uncomment this and continue. Now that we have
this now that we're learning about logging, now that we're
doing all this stuff, we can say If confirmed, we're going to do
some stuff. If confirmed, we're going to create a table called
active item and add this to the active item table. So we're
going to do a little logger dot info bound item. And we'll
create a new table and a new entry in this table. So we'll
say const active item equals more Alice dot object dot extend
ACC active item. This we're saying if active item exists,
great grab it i
f not create it. So we're going to create this
active item table if it doesn't exist, if it does exist, great,
grab it. And we're going to say const active item equals new,
active item. So we're going to create a new entry in this
active item table that we're creating. And we'll say active
item dot set. And we can set any of the columns we want for this
new table that we're creating. So let's give it a marketplace
address column. So we'll say market place address. And this
will come from the req
uest dot object dot get address all of
these requests from events come with the address that they're
coming from, which for us is going to be the marketplace
address, we'll do active item that set and if T address which
these events saved come with all the parameters of our event. So
we'll say request that object dot get NFT address will get the
price will say active item dot set price is going to be request
dot object dot get price will get the token IDs will say
active item dot set token ID re
quest dot object dot get token
ID and then we'll get the seller will say active item dot set
seller is going to be request that object dot get seller. So
we're getting all of this information from our event. And
this event update from Ross automatically always comes with
the address that the event was omitted from. So we're gonna
grab all that we're going to create this active item table.
We're going to add all these rows. We're going to add this
one row with all these columns in it. Awesome. No
w we'll just
do logger dot info just to do a little print out. We'll say
adding address. We'll do a little string interpolation
we'll say request dot object dot get address period token ID with request dot object dot get token
ID. And I need to close this off here, better. And then outside
of the logger dot info, we'll just say logger dot info,
saving. And then we just run await active item dot save. And
now we have cloud function that's going to create a new
entry in a new table called active i
tem anytime item listed
happens. So after item is called the trigger for our cloud
function, and there are a whole bunch of different triggers for
different Ross Cloud Functions. If you go to the Morales docs,
we look for trigger, we can find a list of all these different
triggers in here, like after save for Save. After save,
before delete, after delete, before save file, there's all
these different triggers to trigger this cloud code. Now, if
we upload this new script to our morale server with
yarn morass,
cloud changes uploaded correctly, okay, great, we'll
kill it. Let's go to our cloud server, do a little refresh just
to make sure that it's not still processing that update. Okay,
CPUs low enough. Okay, great. Now in our database, we don't
see an active item table in here. But if we go back to our
Hardhead script, and we call mint and list, since now we have
a Cloud Function that says okay, anytime an item listed event
happens, update that active item table, we should see active it
em
update. So let's run this. And remember for all of this, we
need to have our heart head node running connected to Hardhat.
And if we reset our local chain, we need to click that reset
local chain button. So we went ahead, we ran this. Now if we go
back to our database, we give it a little refresh. And right now
I actually don't see anything. So if I go to my logs go to
info, I can see any errors or issues in here. So it looks like
after save failed for item listed for user bla bla bla,
looks
like there is an issue cannot read properties of
undefined reading extend. And that's also in the info, I made
an issue. I didn't quite write all my code, right. And if we go
back to our update active item, I can see where I messed up. It
should be Morales dot object with a capital O dot extent. So
what I'm going to do, then run yarn morass cloud again, now
that I have this correct, we're going to cancel that, we're
gonna run our mminton list again, now that we've fixed our
script. And now that
we've fixed our script, go back to our
database, we'll give it a little refresh, I can now see we have
an active item entry in here. Now at this point, there
are going to be times when you don't want to leave and go get a
coffee, right, or go to the bathroom or go get some food.
And you're going to want to stop your terminals from running. So
let's actually practice restarting everything and re
getting into this local development environment. Because
it can be a little weird and a little tricky.
So let's practice
this. So once again, let's come over here. And what do we need
to do? Well, we're going to Ctrl C, we're going to kill our
blockchain Ctrl C, we're going to kill our connection to our
Morales server. And if we're running a front end Ctrl C, that
too, now if we go to our server, we go to view details. Dev chain
proxy server, if we hit status, this reset button here will
still we will be disconnected now. And now everything has been
disconnected. Now if we want to restart everyt
hing, if we're on
our heart hat NFT marketplace will run yarn Hardhead node, and
that will spin everything up again, we'll run yarn Morales
sync to sync back with our routes connection, we can go
back to our server will do view details and we should be
connected now. Connected. Since we restarted our local
blockchain, we now need to remember to do reset local
chain, we'll go ahead and run that. Great. If we want to
restart our front end, we can restart our front end like so
now the thing is, our
database will still have even when we
refresh it even though we reset the local blockchain, it'll
still have all this stuff in it. Now these entries in here are
entries from a blockchain that no longer exists. So what I
often will do is I'll click this button up here, and we'll just
delete all rows in this class. To confirm we do active item,
read the name of the table. And let's do it for item listed too.
We'll select that edit, delete all rows, item listed. Yes,
delete. We'll do a little refr
esh. Now everything is
zeroed out here. Now we have an empty database for these events
in our after save here. And now that we've added that little
weight in our script, let's go back to our hard hat NFT
marketplace, we'll run yarn, RT hat, run scripts, mint and list
dot j s dash dash network, local host. This will mint it approve
it listed and then we mined two blocks to give Morales time to
index our event and then on a morale server. We go ahead and
refresh we now see item listed as one and a
ctive item is One
all at the same time. So that is how we're going to make sure
that Morales always indexes whenever we call a function,
we're just going to mind one additional block to tell Morales
Hey, that transaction has indeed been confirmed. So really
exciting. And we got to practice closing and restarting and doing
all that good stuff, too. So now this is fantastic. Now that we
have this additional functionality to make it a lot
easier for someone else over okay, awesome, we can check
act
ive item. Well, we're not quite done yet, right? Because
what if somebody buys an NFT, or sells an entity we should have
active item be removed? Right, right now there's one item
listed in one active item. But if we buy an item, active item
will still show that that item is active. So let's go ahead and
let's update our cloud function to also say, okay, anytime an
item is bought, we remove that item from being active. So let's
create another after save. Let's first built this for canceling
the i
tem. And then we'll build one for buying the item. So to
make another after save to make another trigger, we'll say
mirallas dot Cloud dot after save, we'll say item cancelled.
And this will be an async function that takes the request
as an input parameter again, and we're going to do the same
thing. We're gonna say const confirmed equals request that
object dot get confirmed, we'll say const. Logger equals routes
dot Cloud dot get logger, like so. And then we'll do logger.
Oops, lowercase L. An
d then we'll do logger dot info,
marketplace, the little pipe object and then just request dot
object. And then we'll do the same thing. If confirmed. If
this transaction is confirmed after one block, we're going to
remove it from active item. And we're going to be using a query
to first find that active item that's getting cancelled. And
you can learn more about basic queries in the Morales
documentation here. So we're going to get that table by
saying const active item equals more outlets that
object for the
capital O object dot extend active item. And we're going to
create a new query. So we're going to query our table before
we actually set or save anything. So we're gonna say
const, query, query equals new Morales dot query of active
item. So we're going to query our Morales database to find an
active item that's in there, that's going to match the
request here, so we can cancel it. So we'll say query dot equal
to marketplace address, comma
request that object dot get address, we'
re looking for an
active item where the marketplace address is going to
be the same as the address of the item cancelled. We'll say
query dot equal to NF T address, comma request dot object dot get
an F T address will say query dot equal to token ID, comma
request dot object dot get token ID. And that should be it right?
So let's look again at our contract here. And what is the
item canceled give us it gives us a seller NFC address and a
token ID seller and ft address and a token ID. And we're
l
ooking for NFT address and the token ID, we don't need to look
for the seller, we just need to look for these two. And then of
course, the marketplace address. So great. So now that we have
those two, we can say logger dot info, and then we'll just print
out marketplace type query. And then we'll just print out this
query that we're running. And then we can say const, cancelled
item equals await query dot first, we're going to find the
first active item in the database that has the same
marketpl
ace address and a T address and token ID that just
got cancelled. So we're going to find that first cancelled item.
We'll do a little bit more logger information, we'll say
logger dot info, marketplace, pipe, cancelled item. And then
we'll just do some string interpolation. And we'll print
out that canceled item. Cancelled item and we'll say if
cancelled item. So if the query doesn't find anything, it'll
return undefined. So we're saying if cancelled item which
will return true if it found somet
hing. So if cancelled item,
then we're gonna say logger dot info. Deleting and then we'll do
request dot object dot get token ID at address, request dot
object dot get address address space since it was canceled, so
we're going to do a little print little logging here. Take the
leading that thing since it was canceled and then we're going to
run a wait canceled item dot destroy and that's when we
remove it from the active item. And then we'll just say else.
Logger dot info, no item found with ad
dress, request dot object
dot get address, and token, Id request dot object, dot get
token ID. So cool. So now we have this after save here, it
looks like my terminal automatically added this require
in here, which we don't want. So I'm just gonna go ahead delete
that. We can upload this to our Morales server by running yarn,
Morales cloud and great changes uploaded correctly. And now to
test this test that this is working, let's create a new
script in our hard hat NFT marketplace called cancel
item.
So we'll go to scripts right now we have mentioned list, we'll do
new file, we'll call it cancel dot Jas. We'll do cancel item,
Jas. And this will be a script. So we're going to use that that
main thing here, but we're going to call our function cancel. So
we'll do async function cancel, and then at the top, we'll say
const token ID equals Now let's go to our active item list. And
let's find a token ID that's in here. Okay, token ID zero. And
so we'll use this as the token ID that we want
to delete. So
we'll use token ID zero. So in our cancel item.js script, we'll
say const token ID equals zero and let's cancel it. So we'll
say const NFT. Marketplace equals await ethers dot get
contract. And yes, const ethers equals require hardhat. And ft
marketplace will say const basic NF t equals await ethers dot get
gets contract. Basic NF team do const TX equals await NF T
marketplace dot cancel listing basic NF T dot address token ID.
And we're going to call cancel item should be cancelle
d listing
we call the cancel listing. So cancelled listing Yep, like
that. And it takes the address of the NFT and the token ID. So
the basic NF T dot address and the token D Okay, great. And
then we'll do a weight TX dot weight one. And then we'll do
console dot log NFT cancelled. And then we'll say if network
dot config dot chain ID equals equals 31337. We'll go ahead and
we'll do a wait move blocks to two and then we'll say sleep.
Mount equals 1000. And then we'll just say const. Move
blocks
equals require dot dot slash utils slash move blocks.
Okay, cool. That looks really good. So let's go ahead and run
this yarn hardhat run scripts cancel item.js dash dash
network, local host and ft cancelled moving blocks
sleeping. Okay, great. Our node is running. Awesome. We're
connected to our mirallas we've uploaded our cloud function with
yarn Morales cloud. Now if we go back to our database, do a
little refresh. Looks like I have an issue here. I go to my
info it says after save failed for
item Cancel for user blah
blah, blah. Here's the login information. Morales dot cloud
apt get latar is not a function. Uh huh. Well, that makes a lot
of sense. Get water. Let's do let's make this get logger.
Shall we get logger to let's re upload them. Changes uploaded
correctly. And now I'm going to have to manually go to active
item I'm gonna have to manually delete this one will have a
delete this row. Yes, delete, do a refresh. And the reason I have
to do that is because it's already been s
aved. And we're
doing an after save. So because I messed up. If you spelt that
right, you probably did, right. But because I messed up, we're
gonna have to rebuild a new one. And then delete that new one.
I'm going to run yarn, hard hat run scripts, mint and list
network localhost. And we just meant to add a new one. Let me
check the Morales database, we'll do a refresh. I can see it
in here, I can see it an item listed. It has a token ID of
one. So let's go ahead and cancel that now. So I'm goi
ng to
change my token ID and cancel item to one. And now we'll run
that script Barnhardt at run scripts cancel item, network,
local host run this NFT cancelled moving blocks. Now
we'll go to the front end, we'll do a refresh. And we can see
it's been removed from active item programmatically, which is
great. So this is where these logs can be really helpful. Now
it can be a little scary to do things wrong on purpose, but
learning how to use information like the logging and learning
how to debug
effectively is going to make you a lot faster
of a coder because guess what, you're not going to be perfect.
You're going to run into issues understanding how to use The Log
Understanding how to read the errors is going to make you much
faster developer. So now we have something for cancelled item,
we're also going to need something for what? Well, you
guessed it for buying the item. So let's make another one of
these murales to cloud that after save. And we're going to
be using most of this sam
e exact code for item bought that we use
for item cancelled, we probably should turn it all into a
function. But for practice, we're just going to go ahead and
do it one more time, we'll do Morales dot cloud. And then my
VS code keeps sticking this in for some reason. I'm going to
undo that Morales dot Cloud dot after save. Item bot is the
event. There'll be an async request little arrow function
here, say const confirmed equals request that object dot get
confirmed will get whether this transac
tion is confirmed. We'll
get the logger I'm just going to copy paste so I get it right
this time. So I'm going to copy and paste those two lines const
logger equals Morales dot Cloud dot get logger logger dot info.
And we'll say If confirmed const active item equals Morales dot
object dot extend active item const query and for this query,
I'm actually just going to copy these lines because this is
going to be exactly the same. We're going to look for the NFT
address and the token ID and the mark
etplace address. And if we
look in our NFT dot soul and up to marketplace dot som alright
and bot event as the NFT address. And the token ID which
is what we want to find are listed and of team. So we'll run
that query. We'll do const bots item equals await query dot
first, and we'll do exactly what we did before. If bots item then
logger dot info, deleting request dot object dot get
object ID await bought item dot destroy logger dot info deleted
item with Open ID request dot object dot get open
ID at
address request dot object dot get address. And then if we don't
find it will say else. Logger dot info, no item found with
address request that object dot get address and token, Id
request that object dot get token ID. Cool. So that looks
good. Let's go ahead and upload this to the cloud. So do yarn
oralis Cloud changes uploaded correctly. Let's go make sure it
looks good on our server. So we'll give a morale server a
little refresh. Go to Cloud Functions here. I can see the
item cancell
ed in here still. And now I can see the item
bought after save perfect. Looks like I'm at 100 percent capacity. So
we're gonna give it a second just to cool down thinking give
it a little refresh and looks like we're back down after our
cloud function has been uploaded. Okay, cool. To test
out that this part is working. Let's go ahead we'll write
another script here. Do new file by item dot j s. And we'll do
the same thing right now. On our database. We don't have any
active items. So we'll just
run real quick. We're on mint and
list. Mint a new one. We'll go check our database we'll do a
little refresh. Looks like active vitamins in here with a
token ID of two now. So we'll do is we'll buy that token ID so
we'll say const ethers network equals require arhats const move
blocks equals require dot slash utils slash move blocks const
token ID equals to async function by item const NFT
marketplace equals await ethers dot get contract NF T
marketplace const basic NF t equals await ethers do
t get
contract basic NFT coms listing equals await and ft market place
dot get listing basic NF T dot address and the token ID will
say const price equals listing that price that to string and
we'll say const Tx and this is us going to actually buy it
equals await and ft marketplace dot buy item basic n f t dot
address, token ID, comma. And then the value of course is
going to be the price. Do await TX dot wait one, console dot log
bots NF t. And then if network dot config dot chain ID equals
31
337, then await move blocks to comma, sleep amount equals 1000.
And then this is a script of course, so we're going to use
the same stuff we're using for cancel. But instead of
cancelled, it's going to be called by item. So we have the
item inactive item here, we run yarn, art hat run scripts by
item.js Dash network, localhost. Now we can test buying this
item. Okay, Bob, the NFT looping blocks, we'll do a little
refresh on our database, and boom, we can see the active item
is gone. And we can s
ee, the item has now been bought.
Awesome. We're almost done keeping our active item, just a
table of active items. But there's one more thing we should
do. We're not going to test this here. But if you want to test
it, we can we actually in our NFT marketplace, go to
marketplace dot Sol, we actually have an update listing function
as well, that also admits an item listed. So we also want to
check to see if item listed is coming from update listening. So
back in our item listed Cloud Function, b
efore we actually
start saving stuff, we want to check to see if it already
exists. So we're gonna say so we'll say const query equals new
mirallas dot query. And sorry, I keep sticking the sin of active
item. And we're going to do exactly what we've been doing.
We're gonna say query dot equal to NFT. Address, we're gonna
look for the NFT address request dot object dot get and ft
address, query dot equal to token ID, request that object
dot get token ID, query dot equal to marketplace, address,
comma request dot object dot get address query dot equal to
seller requests dot object dot get seller will say const
already listed item equals await query dot first. And then we'll
say if this item has already been listed. Then we'll go ahead
and say logger dot info. Deleting already listed request
dot object dot get object ID. And we'll do a weight already
listed item dot destroy. And then do logger dot info, deleted
item with token ID request dot object dot get token ID at
address request dot
object dot get address since it's already
been listed, if the object has already been listed, we know
that it's coming from this update listing function. So
we're going to delete it first, and then we'll resave it with
its new price. So and let's just go ahead, and let's upload this
to the cloud yarn Morales cloud. upload this to our server
changes uploaded correctly. Let's go check our server, give
it a little refresh. We'll go check Cloud Functions. And it
looks like our item listed query for
deleting is now in here. But
with all that, we now have a way to constantly have this active
item table only be the items that are actively on our
marketplace without having to spend any additional gas in our
application. And this is going to be way better for user
experience because they're not going to have to pay extra gas
to keep all these entities and maybe an array or some more data
structures. If you've made it this far. This is easily one of
the hardest parts of this course, because we'
re working
with a ton of technologies. We're working with a smart
contract, we're working with Cloud Functions, we're working
with a back end database. Now if you've made it this far, you
should be incredibly incredibly proud. So now let's just go
ahead, let's mint and list one more NFT. So we'll do we'll run
yarn heart at run scripts, mint and list network local hosts
will meant it will list it or sleeping. Let's go check our
database. We'll do a refresh. We see the active item in here. And
now
let's learn how to call all of the objects in In our active
item database here, let's do it. And remember, if you ever reset
your local blockchain, you're gonna have to come in here and
delete all the rows in the support tables. With all that being said, it's
time to finally come back to our front end, and come back to our
index dot J. S and answer this question. How do we show the
recently listed NF Ts, we only want to show the active NF t's
on the marketplace. And now we have a system for get
ting only
the active ones, only the ones that are currently on the
market, because we're indexing these events. Now, I'm going to
delete all these comments here. And let's do this. So what we're
going to do is we're going to do this thing called use Morales
query. So if we go to the React Morales docks, there is a hook
called use Morales query. And this allows us to fetch and make
queries to our database in a React context. Back in here,
we're going to say, import, use Morales query from react
m
irallas. Now, if you look in the docs here, use mouse query
returns data error is loading. And this will automatically run
this query the instant our index pops up. So to get the data from
the query to get all of our active items from our database,
we'll say const data, and we'll rename data to listed and fts.
And then we'll also check to see if this query is still fetching.
So we'll say is fetching and then we'll rename that to
fetching listed NF t is equals use Morales query. And inside
here,
this takes two input parameters, it takes the table
name to do the search on and then it also takes a function or
the query. So the table name that we're going to be looking
for is going to be active item. And then the function for the
query is going to be we're going to say query dot, we'll limit it
just to 10. So we'll say only the first 10, we'll do it in dot
descending order, based off the token ID. And then if we wanted
to do different pages, we could do this thing called dot skip
with page
numbers, we're not going to do page numbers here.
So we're just going to leave it like this for now. And that's
it. So we're saying, okay, great, grab from our database on
the active item table, grab just the first 10 in descending order
of the token ID. Now it's going to save the result of this to
this listed NF TS section. Now to see if this is working. Let's
just do a little console dot log listed NF t's just to see what
this using Ross query actually returns for us. And now we have
our loca
l blockchain Node running, we have our connection
to our Morales server, and we have our front end running. So
let's go to our front end, we'll do a little refresh here, we'll
right click and hit Inspect, we'll go to the console. And we
see we have this array being spit out here. Now the first
time it console dot logs, it's empty. This is because when it
initially loads listed out of T's hasn't returned yet. And
it's so it's actually just going to be an empty array. But when
it finishes loading,
we're going to get an array of size one, we
get an array of size one, because active item only has one
enter right now. So we get this array of size one, and we can
see it index zero, we have class name active item, we have the
item ID, we have all these attributes, which are going to
be created at the marketplace address. And if the address the
price, the seller and the token ID this is exactly what we see
in our database here. So Perfect. That's exactly what we
want to be able to show these N
F t's on the front end. So how do
we actually show this NFT and list this NF T. For people who
aren't developers and aren't going to go into the console dot
log? Well, what we're going to be doing is in this return here,
we'll put some parentheses around this. First, we should
check to see if we are fetching those listed NF T's. So we'll do
some JavaScript stuff. And we'll say fetching listed NF T's and
we'll do a ternary operator. So we're gonna say if we are
fetching those NF T's, let's add li
ke a little div div that just
says loading that that done, we'll put a little colon here.
And if we're not fetching, we'll do we'll say listed NF TS dot
map. So dot map basically loops through and does some function
on all of the listed entities in the function we're going to want
ours to do. And it's going to take each NF t as input
parameters. So we say we're going to basically loop through
each NF T. And we're going to say, console dot log and F T dot
attribute attributes with an S. And then
inside of these inside
of this attributes are the different pieces that we want.
So we're going to get those pieces we'll say const we want
to show the price. The NFT address, the token ID in the
marketplace address, which of course is just gonna be this
one, and then the seller, that's all this information that we're
going to want to show on the front end will say that equals
NF T dot attributes. So we're going to pull these out, and we
can see price seller token ID, etc. We're going to pull th
ose
out of NF T dot attributes. And we can show those by in this
function here, we'll say return. And then we'll return some HTML,
we'll do like div div. And then we can say like price, price,
price, period, and f t address and ft
address, token ID, token ID seller, then added this for some
reason. So I'm going to delete that add $1 signs before all
these for some reason, delete those, save. Now if I go to the
front end, I can now see information about our NFT from
our database is listed here. T
hat's fantastic. We see the
price we see the address, we see the token ID we see the seller.
Now if we go back to our NFT marketplace, our little our
little hard hat MC marketplace, let's mint another one. Yarn
hard hat run scripts mentalist Dash network localhost, we're
going to run that it's going to mint one more. If we go back to
our Morales database, we do a little refresh on that active
item table, we now have a new item in here. So if we go back
to our front end, we give this a little ref
resh. And boom, now we
have two items in here. So this is awesome, we now have a way to
actually show the most recently listed NF t's on our
marketplace, plus ah. Now, of course, you might be
saying to yourself, hey, Patrick, that's cool and all.
But that looks really ugly. And I would agree with you. But 100 percent
agree with you. So we should come up with a component to show
our listed entities that looks a lot nicer. So when instead of
returning and just printing out the raw information, we
probably
want to show the image, right, we want to show the image, we
want to make everything look a lot nicer. So we're going to
create a new component that we're going to return in here to
format all of our NF T's appropriately. So we're gonna go
to components, we'll do new file, and we're going to call NF
T box dot j s. And this is where we're going to grab all the
information on how to show what our NFT actually looks like. So
let's get started working on our entity box, we're gonna set this
up, the way we've been setting all these up would do export
default function, and ft box. Now something that's a little
bit different for this one, though, is that in our index, we
have all this information. So we're gonna need to pass all
these variables to our NFT Box component. So to do that, we'll
add them as input parameters for our component here. So we'll say
price, and if the address, token ID, marketplace, address, and
seller. So right now on our front end, we just have a whole
bunch o
f text, and we even have this gross warning, we're gonna
get rid of that tip. And as we know, tokens have their token
URI, which points to an image URI or an image URL of what the
actual token looks like. So what we're going to want to do is
we're going to want to call that token URI, and then call the
image URI to show the image. So we're going to actually have to
wait those two API requests to get the actual image. And we're
going to save that image as a state variable on this component
here.
So as you already know, we're going to work with use
state to keep track of that image URI. So we'll do import,
use state from react like this. And in here, we'll say const,
image, Uri, comma set image, URI equals use state. And we'll
start it off as a blank string. Now let's create a function,
we're going to call it update UI to update our UI and grab this
token URI and the image URI. So we'll create an async function
called Update UI. And in order to get the image first, we're
gonna need to ge
t the token URI, and then using the image ag from
the token URI gets the image. So first thing we're gonna have to
do is get the token URI. So we know how to do this with use web
three contract. So we'll do import, use web three contract
from react Morales. And as we know, use web through contracts
is going to need some parameters. So we'll say const
run contract function, get token URI, equals, use web through
contract. First, we need the ABI of the NFT because we're gonna
need to call token UR
I. So to get the ABI we're gonna need to
once again update our front end. So let's comment this part out.
We'll go back to our hard hat piece. And let's look in our
deploy scripts. We have this update front end right now. All
this is doing is updating calm object addresses. Well, that's
good. We're also going to want to add API's to our front end as
well. So let's create another function in here called Update
API. And we'll pass the API's as well. So we'll do async function
update API. And we'll
give it both the basic NFT API and the
NFT marketplace API, because we're going to need both of
them. So we'll say const NF T, marketplace equals await ethers
dot get contract. And f t marketplace, we're going to
write the ABI to the front end API location, we have the front
end contracts file. So let's also do a const. Front and ABI
location equals, and we'll do dot dot slash next, Jas, and if
T marketplace dash FCC slash constants, and instead of
actually just giving the file name, we can jus
t give it the
front end API location. And then we'll actually have it generate
that file for us because we're just going to overwrite the ABI
file anytime we work with it. So now that we have the
marketplace, we'll just do Fs dot right file sync, and we'll
do front and ABI location, and ft marketplace, dot JSON. And ft
marketplace, dot interface that format ethers dot utils dot
format types dot JSON. So we're also going to want to do that
for the basic NF t. So we'll say const basic NF t equals
await
ethers dot get contract basic NF T Fs dot right? file sync, it's
going to be that exact same place right here. Except for
it's going to be a different location, it's going to be basic
NFT dot JSON. And of course, we're gonna do a comma basic NF
T, that interface dot format ethers dot utils, dot format,
types dot JSON, you can find this NFT marketplace that
interface in the Hardhead documentation. And you can find
this in the ethers documentation. So now we have
this update ABI function. Le
t's also add this to our module that
exports so we'll do a weight up the ABI like that. There's a hyphen here that
shouldn't be here. And we'll run just this part of our hardhat
front end, run yarn, hard hat, deploy dash, dash dash tags, the
tag here is front end, front end. And now it's had nothing to
compile updating front end, it's done. So if we go back to our
front end, now, we go to our constants, we now see two
objects in here, which are going to be API's, the basic NFT, and
the NFT. Mark
etplace. Awesome. So now that we have that, we can
import those into our front end. So we can say import, and if T
marketplace, abi from dot dot slash constants slash NF T,
marketplace dot JSON. And we can also get the NF T API. So we'll
do import NF T, abi from that the slash constants slash basic
NF T dot JSON. Now in our run contract function, our token URI
function is part of the NFT API. So the API will be the NFT, abi,
the contract address is going to be the address of
the NFT, which we're
passing in as a parameter. So we'll pass an
NF T address, the function name is going to be token URI, and
the params are going to be the token ID, which is getting
passed as an input parameter to this function to this component.
Right. And we can double check. We're good to our basic NF T dot
soul, right? We scroll down, we have this token URI that we're
overriding. And this is the function we want to call takes
the token ID. So this the function we want to call takes
the token ID. Okay, great.
So in our update UI, first, we'll say
const, token URI equals await token URI. Now let's do a little
console dot log token your eye, just to see what this returns to
make sure that update UI is called, we'll add it to a use
effect, we'll say use effect. And this takes an input
parameter of a function to do, we'll just say Update UI. And
then we'll only have this run anytime is web three enabled
changes. So we want to run update UI, but we want it to be
dependent on is web three enabled. And the
n we'll say if
it's web three enabled, then update UI. So we need to add use
effect as well. So we're using use state, we'll do comma, use
effect. And now we should at least be reading our token URI
off the blockchain. We're not going to set the image yet,
right, because we're going to get the image URI from the token
neuron. Let's add this NFT box to our index to see if it's
working well so far back in our index Next, up at the top, we
will import NF T box from dot dot slash components slash NF
T
box. And down here, while we're returning this will add our NF T
box component will make sure to pass in all the parameters it
takes. So the price is going to equal that JavaScript price. And
ft address is going to equal the JavaScript entity address. The
token ID is going to equal the token ID, marketplace address is
going to be marketplace address seller is going to be the
seller. And you saw that warning where it's saying, hey, all the
components need to all the things in the mapping need
to
have their unique key. So we'll say key, we'll give these all a
key as well. We'll say key equals this, we'll do some
string interpolation, we'll just say the NFT address, combined
with the token ID can be the key. So if we save that, go back
to our front end here, do a little refresh marketplace
address is not defined market place address. So let's make
sure we spell things right, let's go back to the front end,
we'll give it a refresh is what three enabled is not defined.
Oops, excuse me in
the NFT box, we need to grab that from us
Morales, so we'll import use Morales. And in our ponents here
we'll say const is web three enabled equals use Morales.
We'll save that. And one thing I noticed actually is this needs
to be wrapped in squigglies. Sorry, I forgot to do that. Our
component actually just takes a props, props input parameter.
And we would need to do like props that token ID to get token
ID. But instead, we just extract it out by doing putting the
little squiggly brackets her
e. So put the squiggly brackets
there. Great, we'll do a little console dot log token, your eye,
or index.js has everything updated. Here. Let's do a little
save. And we can even say if is web three enabled up the UI.
Like that we'll save we'll go to the front end, we'll do a little
refresh. We'll see if everything's working as
expected. And as long as we're on that hard had localhost and
our meta mask. And again, you can ignore this error, this
warning that's up here for now. And if you click t
his, and you
have your IPFS and brave or your IPFS companion, we can now see
we're getting our token URI, which is perfect. The piece that
we want now is this image bid. And for this one that I'm using
it is an HTTPS, which technically isn't decentralized,
right, we would need it to come from instead of HTTPS, we would
need to come from IPFS colon slash slash but but actually
having it as HTTPS ipfs.io. For now was good, we'll explain why
in just a second. Now that we are getting the token URI,
we
can call this URL, and we can get back the image that we want
to actually show on the front end. So in here, we'll do a
little console dot log, the token, URI is string
interpolation, like this, and then we'll say if token URI,
we're going to need to now grab this token URI, and get the
image from it. And this is where we're going to get a little bit
funky. And we're going to cheat a little bit. Now for our
application, not everybody is going to have IPFS companion,
not every browser is IPFS
compatible. So we're gonna have
to actually cheat a little bit here, we're actually going to
change the token Uriah from its IPFS edition to an H HTTPS
edition. And this is known as using an IPFS gateway, which is
a server that will return IPFS files from a normal URL. So
we're going to use an IPFS gateway, which we can just make
regular HTTPS calls to, and it will return those IPFS files. So
technically, are we making the centralized doing this? Yes. And
is that ideal? No. However, until the wo
rld adopt IPFS, and
until the world adopts the standards, it's kind of what we
have to do right now. Because otherwise, the front end will
just show up as blank to them. And we can't have that we don't
want that. So we're gonna say const request URL equals token
URI dot replace IPFS, slash slash with HTTP s, HTTPS. slash slash ipfs.io/ipfs/so.
We're saying, if you have a token URI that starts with IPFS,
that's great. But we're going to switch it to using an IPFS
gateway. It's really use the IPFS
gateway provided by the
team that builds IPFS. So pretty reliable gateway is is kind of a
cop out. Yes. Are our files still on? IPFS? Yes. So it's not
the end of the world. But this is just going to make calling
these API's a lot easier for us. And we're going to say const
token URI. Response equals await. And this is going to be a
little weird. We're going to do two awaits. await a weight fetch
request, URL dot JSON. So fetch is a keyword you can use in
JavaScript to fetch or get a URL fetch k
eyword is essentially
doing the same thing as pasting this into the browser, like so.
And getting this JSON response. So we await to get the response.
And then we await to convert the response to JSON. And that's how
we get the toggle response. So we now have this object in our
JavaScript, which is perfect because this object has this
image attribute that we want. So we're gonna do the same thing we
did here, we're gonna use the IPFS gateway, this one's already
using HTTPS that ipfs.io. But if i
t wasn't, we would still want
to convert it. So now we're gonna say const. Image URI
equals token you are i response dot image. So we're gonna get
the image tag of this response here. And then we're going to
say const. Image URI URL, is going to equal to, and we're
gonna do the exact same thing that we did up here, we're going
to use the gateway image URI dot replace IPFS, colon slash slash
with HTTPS ipfs.io/ipfs. And now, and that's how we get this
URL right here. And so we can finally do set
image URI, to
that image URL. And now we have our image URI is going to be
that image right here. Now, is this a little bit wonky? Yes.
Are there better ways that we can do this? Yes, there's
actually a number of better ways that we could do some of this,
we could actually, since we're using Morales, we could render
image on our server and just call our server, what else could
we do well, or test nets and maintenance routes actually
comes with a bunch of hooks like us NFT balance that will show
us
an F TS show us how many NF T's shows all this information about
NF T's but it only works on test nets and main net, we'd have the
world adopt IPFS. So we don't have to do this wrapping.
Unfortunately, it doesn't yet so such as life. But now that we're
setting the image, alright, we have this image URI, we have
what this actually looks like, we're going to have this and if
we click on this, we use this in our browser, it returns this
dog. So now we have the image URI in our website, we can
fi
nally use it to show what this is going to look like. So
finally, we can create a return in here. So down below, we'll do
return to a little div. And then we'll do another div just
because I want to. And we can do some JavaScript, we can say if
if that image array exists, we'll do some stuff. Otherwise,
we'll do some other stuff. So if it doesn't exist, maybe we'll do
a div for now. div that just says loading dot the dot. And if
it does exist, for now, we'll just say, just do a little div,
close
the div. And we'll just say found it. Now if we go back
to our front end, let's see if we're good here. Uh huh. If I do a refresh, we
see found it for both of these NF T's. Okay, cool. So how do we
actually show these NF T's, we finally have the URL that we can
use to show the NF T's. But we want to actually use them next.
Jas actually comes with a component called the image
component that we can use to render images really easily just
by using a URI. Now, because we're going to use this image
tag, and because it does some optimizations on the back end,
that means that this website won't be able to be deployed to
a static to a static site, like IPFS. Because now our website
requires a server technically, requires a server just because
we have Morales. So that might be another reason, we might not
want to, since we're using this image tag, we can't deploy this
statically to something like IPFS is we're going to up at the
top, we're going to import image from next slash image. And we're
gonna down here we're going to say, instead of founded, we're
gonna go Image, we're gonna close it off here, too, we're
gonna give a loader of just a blank function that just gives
us the image URI. Don't worry too much about loader. For now,
we're gonna say the source of the image is going to be the
image URI. And then we'll give it a height of maybe 200. And
then we'll give it a width of also maybe 200. And if we did
this right, after we save, we should see the image on our UI.
So we'll go ba
ck to our website, and oh, my goodness, we can see
the dogs holy cow. This is getting really exciting. We can
see the puppies, we can see the images, we're definitely doing
something right here, which is really exciting. Now I know I
said this before that this isn't a CSS. This isn't a formatting
tutorial, because that's definitely not my expertise.
However, let's make this look a little bit nicer. And we're
going to use once again, the web three UI kit, because the web
three UI kit has a whole
bunch of tools that are really, really
helpful for us to use. So if we go to the web three UI kit, we
can go to that live storybook Another interactive bit, and we
can scroll down to the section that has called card. Or we can
make these little clickable cards and we can display some
information about our nfts. So let's go ahead and at the top,
we'll do import import card from web three UI kit. And now
instead of just showing the image, we'll wrap the image in a
card like this. We'll save that.
Now back on our front end, give
it a little refresh. Now we've got this kind of clickable
section that looks a little bit nicer. We'll even label it. And
we can even label it with a title and description. Now we
can grab the title and the description from the token URI
response. So up at the top, let's go ahead, let's grab the
title and the description of the token UI as a state variable. So
we'll say const, token name, set token name, equals use state,
start off as blank, the const token descri
ption, that token
description, equals use state. Start off as blank to download,
we do this update UI bit, we'll call set token name. So we'll
say name is token, your response dot name, we'll say set token
description, which will be token, your response dot
description. And then we'll use those descriptions and title in
the cart. So we'll say title equals token name, description
equals token description. Save that. We'll look at our front
end here. Give it a little refresh. Oh, and now we have t
he
name of the NFT. And its description on our front end.
Okay, cool. Let's keep going. What else do we want on this?
Well, we probably want who it's owned by. So we'll put a little
div inside the card, say div, maybe we'll even put the token
ID we'll do a little number. With the token ID. We'll do another little div, and
then we'll make this italic. We'll say class name equals
italic. And then text is going to be small. And we'll say owned
by, and this is where we can get the seller that we're
passing in
as an input parameter. And then maybe underneath the image, we
want to put the price. We'll do a little div here. And then
we'll say price like this. However, we're probably don't
want it in way we want it in human readable units. So we'll
import ethers, we'll say import ethers from ethers. And then
instead of just showing the price, we'll do a little, little
JavaScript and here we'll do ethers dot utils dot format,
units, price, price and ether. And then we'll do space eath.
And then
we'll make this be class name equals font bold, so
we can read how much it's listed for on our marketplace. Let's go
back a high we can now see, this is token ID number four. We can
see who it's owned by we'd see the price of it and then more
information about the dog. Yay. Now let's format all this stuff
in here a little bit nicer. So let's wrap everything in a div
in one more div. And that will say class name is going to be
flex. And this is going to help format everything, we'll put
everythi
ng in a column items and get to. And now if we look back
there, kind of like in a column now, wrapping one more div, give
them some padding, last name equals P to say a little bit
padding has been added. Okay, nice. And then we'll go back to
our index, and we'll add some formatting to our index here. So
our main one, we're going to remove styles dot container,
we're just gonna say container. MX auto, we're going to make an
h1 in here, class name, equals, I form E X for font bold text to
XML. Thi
s is we're just gonna say recently listed, then we're
going to do another div. Class, name equals flex, flex wrap.
We're going to end this div around our JavaScript here. And
let's look at our UI. And let's see what that does. Okay, cool.
Let's go back to our index. Let's remove all this stuff.
Since now we're adding that to the card. Let's save. Now it's
looking a lot better, we finally able to start listening our NF
Ts and then have them show up on our marketplace like we see
here. Fantastic.
Okay, if we go to the readme homepage show
recently listen to empties. Oh, my goodness, this is a check
we're done here. Now, and I know what seemed like a lot of work,
but a lot of it was setting up that morale server correctly.
And because we have our own back end, now, we have some back end
services that we needed to configure. But now that
everything's set up, the rest of this is going to be easy St. Now
we are only going to want this to show if we're connected to
web three. Right? Now, if w
e click this, this still shows up.
So we're going to have to update this a little bit. And in our
index.js, we're now we're checking to see fetching.
Listen, NF T's right before that, right? Before we do that
we actually want to see is web three enabled. And this is going
to be a little bit of nested tertiary operations. If web
three is enabled, then we're going to do all this fetching,
listed NF T's stuff here. We're gonna do everything in here. So
if it's not enabled, let's do like a little di
v that says web
three, currently not enabled. And of course, we're going to
need to grab is web three enabled. So we're going to say,
for the top we'll do const is what three enabled equals use
oralis as we've been doing, and we're going to grab use Morales
from react Morales, we'll save that go back to our front end,
give it a little refresh. If we're connected, we'll see the
marketplace. If we disconnect, we see web three currently not
enabled, which is what we want. Perfect. So what is next i
n our
readme. If you only NFT, you can update the listing. Let's first
let's figure out if somebody actually is the owner of these
entities. Let's make it really easy for the people on this
website. Well, first we can get the person's Metamask by
grabbing the account from us mouse, so we'll do a comma
account. So then we can easily just do const is owned by user
equals seller equals equals equals account. So the seller
we're getting from the contract, the account we're getting from
whoever's con
nected here, if the seller equals the account, there
is no seller equals equals equals undefined. We'll just say
they're owned by you. And now instead of saying owned by
seller, we can say const, formatted seller address equals,
and we'll do the ternary operator will say if it's on by
you, then we'll just say you instead of seller. Otherwise,
we'll say seller. Now we'll say owned by formatted seller
address like this. So if we go back to our front end, we do a
little refresh. depending on who's
connected, you might see
owned by blah, blah, blah. Now if I go to my Metamask, let me
go ahead and switch account to account three, let's go ahead
and connect our account three, we now see owned by you instead.
Right, and we can even switch again, we'll switch accounts
again, we'll switch to account one, we now see owned by blah,
blah, blah, like so since when we switch back and forth, the
diameters of this actually change which is really annoying.
So maybe we want to go one step further. And w
e want to truncate
this seller address make it a little bit smaller. So we want
to make a seller a little bit smaller. So let's create a new
function. And we can create this outside of the export default
function because this is going to be a function that doesn't
depend on anything inside our app. It's just going to be kind
of a raw function. So we're going to create we'll call it
const truncate string, and this is going to be a function that
takes a full string, and a string length as paramete
rs. And
we're just going to pass the seller address and how long we
want to make this string. So this is going to be an arrow
function we're going to do here, and we're gonna say, if all
string dot length is less than or equal to str Lang, return
full str. Otherwise, we'll say concept ra torx equals three
little dots. And we'll say, Let's separate Tor length equals
Sep. Ra Tor dot length. To make con separator length, we'll say
const chars to show is going to be the the string length minus
the s
eparator length, we'll say const. Front chars or front
characters is going to equal math dot ceiling chars to show
divided by two const back chars is going to equal math dot
floor, chars to show divided by two. And if you don't understand
this math here, don't worry about it. And then we're gonna
say return full string substring of zero to front chars
plus the SEP IRA tour plus full STR dot substring. of full STR
dot length, minus back chars. And now what we can do is we can
grab this truncate S
TR this truncate string and for is
formatted seller will say if it's you will still do you but
otherwise we'll do truncate string of seller or blank if
there's no seller, and we'll have it be size 15. And now if
we save that, go back to our front end, if it's owned by you,
it's still gonna say on by you. But if we switch accounts, and
now says owned by, you know, blah, blah, blah, dot the dot
blah, blah, blah with a truncated address. And then
these sizes don't actually change, which is a lot ni
cer
than them getting bigger and smaller. So awesome. So now we
have this formatted even better. Okay, now what do we want to do?
Well, now that we know who owns the NFT, NF T, and it's
formatted pretty nicely, we need to figure out a way to update
the listing. So what we want to do is, once again, if it's owned
by us, and we can switch back to the account, it's owned by if
it's owned by us, when we click on it, we want to be able to
update the listing on the marketplace. So to do this,
we're go
ing to create a new component called Update listing
ngModel. So we're gonna create a new component, new file, update,
listing ngmodel.js. A ngModel is something that like pops up. So
for example, this little pop up here is known
as a modal. And this is what we want to build. If it's owned by
us, when we click this, we want it to pop up this modal thing.
So to get started, we'll do the same thing we've been doing for
all of our components here, we'll do export default,
function, update, listing n
gModel, like so. And we're
probably going to want to pass it these parameters from the
entity box, probably gonna want to pass these parameters. So the
ngModel knows what function it needs to call in our NFT
marketplace. The way we're going to update listing is we're going
to call this update listing function where we need the
address token ID and then some new price. So we're going to at
least need those. So we're going to need the NFT address, and at
least the token ID to make this little pop
up, we're not going
to code it ourselves, we're going to once again, use the web
three UI kit, web three UI Kit has this nice pop up section,
where it has some code to work with a modal. So we're going to
import that will do import ngModel from web three UI kit,
like so here's what it looks like we have modal and then we
have all this stuff on working with the modal. So we'll do our
return, do a little modal, like this. One of the key things in a
ngModel is whether or not it should be visible. S
o it has an
is visible tag, which we're actually gonna have to grab,
we're actually gonna have to grab from the empty box. So
we're going to pass a little is visible here as well, in our NFT
box, we need to tell our ngModel when it's visible. So we'll
we'll make that code in a little bit. Right now, it'll just be
blank. And inside of our ngModel, we're going to want to
give an input field for how to update it. So I know we've done
some regular inputs before, since we've been working with
web thr
ough UI kit, let's just use the input that it has as
well. So we'll do a little comma input here. And inside of the
ngModel we'll create a new input. So on our input field,
we'll give this a label which is going to be update listing price
in L one currency. And we'll just do eath. For now we'll just
hard coded as eath name will be new listing price, and type is
going to equal a number we'll do Little backslash here, oops,
we'll do a little backslash instead of like that. So cool.
So when this mo
dal pops up, it's gonna have this input in here.
And we can actually test to see if this is working. We can
import this into our NFT box, we'll do import, update, listing
ngModel. From that slash, update, listing ngModel oops,
and sorry, this doesn't need parentheses, and at the top of
our return here, right before our card will add it in. So
we'll just add another div though. So that these two can be
in the same react. But we'll add another div, we'll say Update
listing ngModel, like so with a
little backslash here. And right
now we just have to pass is visible. And then for now, we're
just going to say true. So now with this listing ngModel on our
front end, we do a little refresh. And we get this little
box like this update listing price and one currency. And we
have to close two of them. Because technically we right now
both modalities are true, right. So if we do a refresh, we get
this little input pop up this little modal and we close it
twice, because we have two NF T's if you h
ave a ton of NF T's
here, you will have to close a ton of those. So we change it to
false, we go back to the front end, we do a little refresh, and
boom, now it's false. So true, save, run, and it's there.
Gross, delete, delete, false, save front end, it's not there
anymore. Okay, cool. So we're gonna have to tell this ngModel
only to pop up when somebody clicks this and have tea that
they own. So to actually toggle this and actually make this
work, we're going to update our card. So whenever we
click our
card, we're going to create a function called handle card
click that handle card click is going to update a variable for
whether or not we should show this ngModel. So we're going to
do is we're gonna say on click of the card, aka, once we click
our dog, we're going to call some function. So we're gonna
say on click, equals handle card, click, animal card, click,
and we're going to make this a function, we're gonna say const,
handle card click equals a function, we'll use some Arrow
s
tuff here. And then we'll just say, if it's is owned by user,
if it's on by user, we'll show the ngModel. Else, we will call
the by item function, since we want our whole UI to rerender.
Once we change, once we change the variable to show the
ngModel, we're going to do this as a US state. So we'll say
const, show ngModel. Come a set, show ngModel equals use state.
And then we're going to start it off being defaulted to false. So
by default, we are not going to show them on down which is what
we
want. But if it's owned by the user, we're gonna say Set
Show ngModel to be true. And then else right now we'll just
put console dot log, let's buy will actually update this to
bind the item a little bit later. So now instead of having
is visible be false, we'll have is visible equal to show
ngModel. Or show ngModel. Variable. Okay, great. So now if
we save that, we go back to our UI, we right click, we hit
Inspect, if we own it, and we click it, the ngModel will pop
up, and we can click it out,
click another one, but that will
pop up if we don't own it. So if we switch accounts, Connect will
switch. We click it, nothing happens. And if we go to
inspect, we go to the console, we click it, we should see let's
buy pop up, which is what we do see, we'll do a little refresh
here. Click we see let's buy pop up, click again, let's buy click
let's pilots pilots, why? Great, cool. So now we have a word for
that ngModel. To actually show up correctly, let's switch back
to the person who actuall
y owns this NFT we click it, we want to
be able to when we hit OK, or submit, we want to send a
transaction to update the price of our NFT here, so what we can
do is in our input will have a label called on change equals
and this is the function that will call whenever this updates.
So we're gonna say on change event is going to be a function.
And we're going to create a function called set price to
update listing listing with event dot target dot value. So
we want to keep track of whatever we'v
e put in here. So
when we call the function to update the price, it'll just
already automatically have it. So we'll create this function
and event dot target dot value is going to be whatever's in
this input box here. So we'll create this set price to update
listing with will have this be a use date because we are going to
want to change the UI based off this so we'll say const price to
up date listing with comma set price to update listing with
equals use state and then we'll have this be zero
to start or
blank. And so now, whatever is in here is going to get updated
with this. So now I can do like our console dot log price update
listing with Get back to the front end, oops, we need to
import use state import, use state from react. Go back to the
front end, we click this, we right click Inspect. By type one, we can see 114, we
see 14. So let's remove the console dot log now. Now what we
can do in here is we can create field called on okay, this is
going to be the function that we cal
l when we hit this, okay
here, so on. Okay, it's going to be equal to a function, we're
going to use a little arrow notation, and we're going to
call that update listing function on the blockchain,
we're going to need to grab that function so we can use it. So
once again, to use that function, we're going to import
use web three contract from react mirallas. And as a new
hook, we'll say const run contract function called Update
listing, equals use web three contract. And this is going to
be a fu
nction that we're going to call on our NFT marketplace.
So ABI is going to be the NFT marketplace API, which we can
get by doing an import, similar to what we did over here. And we
actually just copy paste from our NFT box. So we'll do import
NFT marketplace API from dot dot constant send it to marketplace
that JSON, we're gonna need the contract address, which is going
to be an input parameter to our update listing ngModel. So we
can even do a comma market place address, copy this place that
he
re, that means in our NFT box, pretty soon, we're going to have
to pass all these variables to it, but we'll save that in just
a second. So NFT marketplace address marketplace address
function name, is going to be what it's called Update, listing
in our smart contract, and then the params. So if we go to this,
we have update listing takes the NFT address token ID and new
price do NFT address, which will be NFT address, we'll say token
ID is going to be token ID and the new price your price will
get from price to update listing with but will convert it from
human readable to ethers. So we'll import ethers from ethers.
And the new price will be ethers dot utils dot parse ethers or
ether price to update listing with or just in case it's blank,
we'll just say or zero. So we have the NFT address the token
ID marketplace address in our NFT box, we're gonna have to
pass those parameters in here. So we have is visible, we're
also going to have token ID, which is going to equal token
ID, market
place address is going to equal the marketplace
address. And the NFT address is going to equal the NFT address.
And as we code and test this something that's going to be
really annoying, because it's really annoying for me right now
let's refresh our website. And if we click it, this thing pops
up. But when I hit X, and I click this, again, nothing shows
up. That's because technically show ngModel is still true right
now, even though we've exited out. So what we want to do in
the NFT box is in o
ur update listing ngModel we're also going
to pass it in on close, and we're going to pass it a hide
ngModel variable that we're going to create, and right under
Show ngModel Set Show ngModel we're going to create const Eid
ngModel. And this is just going to be a function, that's just
going to say set, show modal. to false, we're going to pass this
function to our update modal listing. So we're going to do
comma on Close. And in our modal here, we're going to say, on
Close. On, cancel, we're goi
ng to do, we're going to call that
on close function, or on Close button pressed, we're also going
to call that on close function. Now if we refresh our website,
click this modal pops up, we click X, click it again, it'll
pop back up, because now we're properly setting it to false and
then resetting it to true. Now to actually send this update
listing function, we're going to pass this another thing, another
variable, we're going to pass it on Okay, which is going to be a
function as well. It's
just going to call update listing
that we just created. Now, it's always a good idea to add an On
error. We'd say on error, take that error as a function and
console dot log. The error and this needs to be
in squiggly brackets like that, but contract address is wrong.
So we'll make this contract, con tract address and spell that
correctly. Spelled correctly. Now let's go back to the UI,
we'll give it a little refresh. Click, this will add one. And we
see Metamask pop up. So this is working out p
erfectly. Now I'm
going to cancel it, and our apps gonna freak out and stuff. But
that's okay, we are doing fantastically okay app popped up
like that, which is good. We have this little error handling,
which I like to add for all of these run contract functions.
Let's also do an on success. So let's say when this does go
through successfully, we'll call a function called handle update,
listing success. And this will be a function that we'd call
when this goes through correctly. So at the top, l
et's
make this new function, we'll say const. Handle update,
listening success is going to be a new function using the arrow
syntax here. And we'll have this setup a new notification for a
web application. So for us to do notifications, we're going to
use web three UI Kits use notification, this is going to
be that same notification service that we used in our last
one. So up at the top here, we'll import it, use
notification. And then we'll say, right in our component,
we'll say const, dispatch
equals use notification. And since
we're using notifications back on our app.js, we have to import
it in here. So we'll do import notification provider from web
three UI kit. And inside of our Morales provider, we'll add the
notification provider around our header and our component, so
that we have context for this. Now in our handle update listing
success, inside this function will say dispatch, say type is
going to be success. message will be listing updated, title
will be listing updated, pl
ease refresh, please refresh and move
blocks. And then position. Top right. And then we'll do on
clothes, and on clothes. And we'll say set price to update
listing with back to zero. I also I'm editing this a little
bit in the future. And I realized that I actually forgot
to add the TX to a lot of these handle functions. So on these,
whenever we call one of these run contract functions, like
we've been saying they have this on error in this onsuccess. Now
this onsuccess automatically passes the
result of the call to
whatever callback function is there. So for example, up the
listing returns a transaction, and we'll pass that transaction
to whatever you add to the onsuccess. So now in here, you
can actually have it have a transaction as an input
parameter. And this would be the transaction that's going to go
on the blockchain to you know, update the price. So we actually
want to change it from a regular function to an async function.
So we can actually do await TX dot Wait, one, because
we don't
want to say, hey, success, you know, listing has been updated
before the transaction actually goes through. So we want the
transaction to go through first. And then we want to pop the
dispatch up saying, hey, it's gone through so and then
additionally, when we actually call these modells on the on,
okay, on the onsuccess, we pass them in just by referencing the
name of the function. So we don't do this arrow syntax,
anymore like this, we just say, hey, the onsuccess is going to
be this
, go ahead and pass your results to it. So that's how we
actually call it down here. If you look at the GitHub repo
associated with this, you go to components, and we go into
these. So in this video, I forgot to add the await TX dot
wait one. But in the GitHub repo, we have these. And when
I'm demoing things in the video here, the dispatch, it's gonna
pop up before the transaction actually finishes going through.
So just want to let you know, and back to the video. So now we
have a little succes
s thing that'll pop up when we're
successful. And the other thing is, when we call this, we are
going to omit an item listed inside of our Morales dashboard,
the price should actually update in our active item because of
our Cloud Functions. So we're going to put this all together
now. So we're on the front end. And if the marketplace owned by
you will click it. Update listing to 25. We're gonna hit
OK, meta masks is gonna pop up. We're gonna go ahead and confirm
and ran through an error because
we need to click Metamask I need
to reset my account. So I'm going to do settings, advanced
reset account. Okay, now let's go ahead
Metamask has popped up again, we're gonna go ahead and confirm
it closed, the pop up modal, and we got our little notification
there. And we can see an activity we have that
transaction has indeed completed So now if we go to active item,
we give it a little refresh. Right now we see our item listed
event. But the issue is that it's not confirmed yet. So what
we're
going to need to do is we're going to need to move our
blocks by one. So in our NFT marketplace, we're just going to
create a new script, a new file called Mind dot Jas, just to
move our blocks once, and we're going to say const, move blocks,
equals require dot slash utils, slash move blocks, we'll say
const blocks equals to const sleep amount equals 1000 async
function mine, do await move blocks will do blocks as the
parameter and then sleep sleep amount will equal sleep amount.
And this will
be a script. So we'll add our copy paste our
that same syntax we're doing here just with mine. And now we
want to just mine these two blocks. So we'll run yarn, art
hat run scripts. Mind that Jas dash dash network, local host.
So we're going to mine those two blocks. Now if we go back to our
database, we go back to active items, we can see it's been
updated because now in our item listed, that 2500 event is now a
confirmed transaction, and we get to confirm their excellent.
So that means since i
t's confirmed back in our front end,
we'll give this a little refresh. We can see the pup is
now worth 25 eath. Awesome. So we're updating ngModel is
working perfectly. Excellent job. So now let's go to the
readme. If you own the NFT, you can update the listing. That's a
checkmark. Excellent. Next, what do we want it to do? If you
don't own it, you can buy the listing. Okay, so let's go back
to our website, let's switch users to a different account,
we're probably going to want an account that o
wn some money. So
let's go ahead and send this other account some money
transfer between my accounts, we'll send 100 eath to account
one, confirm on our hard hat chain here. Alright, great. Now
we can go ahead and switch to account one, and we have 100
eath. Okay, great. As these pops, each cost less than 100
eath. So that's gonna be more than plenty for us to test this
out. Let's go back to this box. Because I think somewhere we
said, we did a little handle card click, if it's owned by the
user
, have the modal pop up. If not, let's do the by
functionality. So to do the by functionality, we're gonna go
ahead and do another run contract function. So we'll do
const run contract function, we'll call this one by item. And
this will equal use web three contract. And this is going to
be the ABI for the NFT marketplace. API, the contract
address is going to be the market place address. The
function name is going to be by item, the message value is going
to be the price of the NFT. Because we
need to send that
amount to buy the item. And then params are going to be NFT
address, which is going to be the NFT address. And the token
ID, which is going to be the token ID now that we have this
by item on handle card click, we can say okay, said show ngModel
is true. Otherwise, we're gonna call by item. And we're going to
do on air. Air is going to be a function where we're just going
to console dot log the air and onsuccess. It'll be a function
where we call handle by item success. So we'l
l create a new
handler for this right underneath handled cart click
the const handled by item success equals a function. And
for this, we'll also have this do a little notification. So
once again, we're going to import we're going to import use
notification from web through UI kit. We're going to say const.
Dispatch equals use notification. And then in
handled by item success. We're gonna say dispatch type, success. Message. Item
bought. Title will also be item bought. Then pole position will
be
top right That's it. So handle card click, if they own
it, we're going to show that update listing ngModel. If they
don't already own it, someone's gonna buy it. So let's go back
here. And I'm currently on an account that does not own these
NF T's. Let's go ahead and click it. Our Metamask does indeed
show up for 25 eath. That's crazy expensive. Let's go ahead
and confirm item has been bought, we'll go ahead and click
that little x, we go to our meta mask, the transaction is
pending. And it's g
one through. This is fantastic. Okay, our
homepage is done, we can show recently listed NF T's if not,
you can update the listing. If not, you can buy the listing.
And now it's time for our cell page. So the last thing that we
need to do is our cell page, let's get this cell page, let's
get this done pages, we have our cell NFT page, which right now
does a whole lot of nothing. And on our front end, we go here,
there's not a whole lot here. And actually, you can list your
NFT in the marketplace,
we also needed to add withdraw proceeds.
So I didn't add that. But that's probably going to be something
we're going to want to allow people to do as well. So let's
get started here. So we can remove this head stuff now that
we're adding that in our main page. And for us to submit a new
NFT, we're probably gonna need a space to add the address of the
entity that token ID of the NFT and all this other stuff. So
we're gonna need a form to do this, which guess what, we can
also grab a form from th
e web through UI kit as well. So we're
gonna go ahead in our cell NMC page, we're going to import form
from web three UI kit. And we're going to create a new form in
our cell page. Now, the parameters we can add to our
form, again, you can find them in documentation here, or we're
going to add with one of the main pieces is going to be this
data piece, which is going to be an object that has a list in it,
of all the different fields, we can put in our form. So maybe
we'll do our first one have a
name of NFT address. That's
gonna be of type text, excuse me, and these are all going to
be this is a list of list of objects like that. Now, if we
save that, we go back to our cell page, we can now see an
empty address and a little Submit button. That right now
does a whole lot of nothing. So we have an NFT address, it'll
take a text, maybe we'll also do in foot width of 50 percent will have
the starting value be blank. And the key of this will be NFT
address, what else do we need, we're going
to need to give it
the token ID so we'll say name, token ID, type is going to be a
number value, we'll start off it's blank. And then the key for
this will be token ID. Next, we're going to need the price.
So we'll say name will be price. In eath. type will be a number
value will be blank. And the key will be price. And we don't need
to have to form tags, we'll just delete that second one and have
it auto close with one tag. And then in here, we'll do title
equals sell your NF T ID equals main
form. So cool. So now we
can take an empty address, a token ID and a new price with
the title of cellular NFT. Great, that looks really nice.
Right now our form doesn't do anything, we probably want to
give it the functionality to actually do stuff. So we'll say
on Submit, and we'll have to create a new function to
actually list our NF T's. So we're going to create a function
called approve and list we have to approve our marketplace to
pull the NFT from our wallets. So we're going to create a n
ew
function, a sync function approven list, which is going to
take a data input parameter on our form, when we hit on submit,
it's automatically going to pass this data object to our approved
list function. So that's how we're going to get the value of
the address the value of the token ID and the value of the
price. So in our async function approven list, do console dot
log, approving the contract, the NFT address is going to come
from this data object. So it's going to be data dot data at
inde
x zero, or zero with object here is going to be our address
that input result. Our token ID is going to equal
data dot data. One because again, this one is going to be
our token ID that input result. And then the price is going to
equal that ethers dot utils dot parse units of data dot data
have to the input result, comma ether.to string. So we're going
to get the price in eath. In human readable form, we're going
to convert it to Aetherium readable form. And then we're
going to pass it as a str
ing because this returns a big
number, which we don't want. So we have the NFT address, the
token ID and the price of the new listing, what we can do now
is we can say const approve options equals a little function
here, the ABI which is going to be our NFT abi, which we need to
import, we need to import both ethers from ethers. And we also
need to import the NFT abi, for its NFT ABI from dot dot
constants slash basic at A T dot JSON, I just copied and pasted
from the NFT box, the contract addre
ss is going to be the NFT
address, the function name is going to be approve params are
going to be two, the marketplace address, which we're going to
define in just a second, and then the token ID will be token
ID. Now the marketplace address in our NFT box, we're getting
this directly from index, an index is getting it from our
database. Now we want our app to be smart enough to be able to
grab the NFT marketplace itself, the marketplace address. And if
we go to our constants, right now, we act
ually have it in this
network mapping. So we're going to want to grab it right up at
the top by saying const marketplace address equals
network mapping, network mapping of what of the chain ID of the
entity marketplace address at the zero with index. So network
mapping of the chain ID, which we're going to get honest chain
ID equals us Morales. Now chain Id actually comes like we said
it's a wax hex form for Morales. So we're gonna have to convert
the chain ID to its string readable version. So
we'll say
const, chain string equals chain ID. And we'll do a tertiary
operator, if the chain ID exists, will parse int of the
chain id.to string. So we'll parse it from its hex to a more
readable version, and then do.to string. And then otherwise,
we'll just say we're on 31337. So chain ID string. So in the
network mapping at the chain ID string, dot NFT marketplace at
index zero, that's going to be our marketplace address. So we
got the marketplace address, this is all we need to call the
appr
oved function on our NFT we can now call running contract
function for a proof. So we're actually going to do this a
little bit differently. We're just going to say const. Run
contract function equals use web three contract, you could import
just run contract function, and then pass all those options to
it like what we're gonna do here. Now we're just gonna say
await on contract function. Because this is an async
function await run contract function, we're gonna say params
are going to be approv
e options. And we're gonna say onsuccess,
we're going to do something, I'm going to say on error, we're
also going to just do error, little arrow function, console
dot log error. Now on success, once we send this transaction,
after the Approve goes through, we're going to want to call the
list function right underneath this will call async async.
Function, handle approve success. And this is going to
take the NFT address, the token ID, and the price is it put in
parameters. So once this run cont
ract function goes through,
we're going to call of handle proof success. And
we'll pass it the end of the address, token ID and the price,
which will say console dot log. Okay, now time to list and we'll
do the same thing. We'll do const list options, equals and
this is going to be all the options for calling the list
function API is going to be on the marketplace this time. So
NFT market place abi, which again, we're going to have to
import so I'm going to go back to NFT box. We're gonna copy
t
hat line here, paste it in the top. That's going to be the API
for that contract address. It's going to be the marketplace
address, which we already have. function name is going to be
list item. And the params are going to be NF T address of the
NFT address, token ID of token ID, price of price. And now we
have those options, we can do await, run contract function
with params. Of list options, will say onsuccess arrow
function, handle list success, which is a function we haven't
made yet. It wil
l say on air error. And we'll just say
console dot log error. Okay, so let's make this handle list
success handle one spell handle correctly handle the success,
this will be an async function. And this is also going to call
dispatch and make a little notification. So we're going to
grab that from web three UI kit, use notification. We'll say
const dispatch equals use notification. And now down in
handle the success say dispatch, type success. Message NFT
listing, title NFT listed position. top b
ar, and cool. And
we're also gonna have to grab us Morales from react Morales. So
do import us Morales from react or else. So let's import this
network mapping. Import network mapping from dot dot slash
constants slash network mapping dot JSON. Because I spelled
contract address wrong again, good our front end we'll give it
a refresh us web three contract isn't defined. Let's get that
from react moralss. Well save that refresh our front end. And
tada, we're good to go here. Alright, now to test
this out,
we're going to want to do is we're going to want to create
another script here. So we're back in our heart at NFT
marketplace, we're looking at our scripts. And we're going to
create a new file just called mint.js. So we're not going to
list this time, we're just going to mint it just going to mint an
NF t so we can list it ourselves on the UI. And actually, we can
just copy our mminton list code, paste it into here, and just
remove the approval and the listing code. Boom, remove that,
remove this, remove this. And that's all we need. And now
we'll just change the name to mint. Well, we can remove price
as well. And we'll change this to mint. And that's it. Now we
have a script that we can call, well, we'll just mint an NFT, we
can also get rid of the NFT marketplace. Actually, we will
probably want the token ID so we can know what it is. So let's do
const token ID equals. So let's actually get the receipt we'll
do const mint TX receipt, but the receipt, I can there equals
th
at. And then I'm just going to copy this from the mminton
receipt. Cons token ID equals this, paste it into our mint.js
and then a console dot log. Got token ID, string interpolates
token ID, hard hat, run scripts, mins dot j s dash dash network,
local host. And we're going to mint an NF T. Got token ID six
means we know m token ID six, we can list this entity. Let's also
add the address. Let's do console dot log NFTL. And if the
address is going to be we'll do string interpolation. And you know
what? Let's make
this again. Mint dot j s okay cool. Got token ID seven and if
the address this so what we can do now we'll grab this NFT
address. So from account one, let's go to Account three
because that's the account that I've done my imports on now when
you switch accounts, we're going to want to refresh the page as
the address seven 0.6. Submit Metamask pops up. Give
permission Yes. Okay, now it's time to list you now have one
pen, okay. Local host list item. We can see all the data and
e
verything. Okay, let's go ahead and confirm NFT listed Okay,
okay. Okay. Okay. So this is good. Now to get Morales to
catch up to speed back in our node. Let's just go ahead and
run yarn, RT hat run scripts, mine dot j s dash dash network,
local host. We're going to mine those two blocks. And now if we
go to our item listed, we'll give this a little refresh. Go
to active item. We can see there's a new item listed for
0.6 eath. In our database. This is fantastic. Now that we've
listed this, if we
go back to now let's make And let's move on
to one more. Let's list the second one, just to see that
it's actually working. Okay, token ID eight. Let's grab this
address. Let's give this a refresh. paste this in token ID
eight. We'll do 0.999. Let's submit. we'll approve this. Yes.
Then we'll send it. Yes. Okay, now let's go ahead and mine two
blocks. So I'm just going to hit up and go back to mine will run
the mining, moving blocks. Okay, great. Now, if we go back home,
we'll see there are thr
ee NF T's now listed, we have the original
one, that we have those two that we just listed eight, and seven
for 0.6 and 0.999. Listed on our NFT marketplace. This is so
exciting. Our listing is working correctly, okay. Now, due to the
fact that this lesson is already incredibly long, actually
decided to cut the part of that and then withdraw a bit, because
we don't really learn anything new there. However, feel free to
jump back in to get every bit associated with scores, where we
will have that
withdrawal functionality for you if you
want to implement it. Otherwise, feel free to skip and move on
ahead. This is incredibly powerful. And you should be
incredibly excited about yourself. If you've made it this
far. This is awesome. You just made a decentralized marketplace
and then built a front end on top of it to allow anybody to
interact with your marketplace. Easily. Huge, huge.
Congratulations here. Oh, this is a perfect time to go take a
break and celebrate. And this is a great time
to ping me on
Twitter to ping me on Twitter saying hey, Patrick, I just
completed the NFT marketplace. Full Stack front end part of
your Free Code Camp course, I now know how to build full stack
front end on top of my smart contract applications. And be so
so pumped with yourself because this is so awesome. I can't
understate how excited I am that you've made it this far, you are
learning and working with a ton of technologies, solidity smart
contracts, and front end you are doing full stack you
were doing
a lot of stuff here. So you should be really proud of
yourself. huge congratulations, be sure to absolutely give
yourself a pat on the back. And then get ready to continue to
our next section. Now that we've done all of this
using our Morales back end, I'm about to switch it up on you,
instead of indexing all of our events with a centralized
server. Now we're going to learn how to build this using the
graph, which is a decentralized event indexer that we can use, a
lot of the code is
going to be exactly the same. So instead of
us starting from a new, what we're going to do, first of all
we can we can close all our local stuff, we can close all of
these things, we can close all of our terminals finally, which
is really exciting. And in this folder, what we're going to do
is we're actually just going to copy everything into a new
folder. So I'm going to CD down a directory, we're going to make
a new one called next Jas. And if T marketplace, the graph,
dash FCC, and all the c
ode for this section is going to be here
for front end the graph indexer we're gonna have to make another
repo and we'll get to that in a little bit. For all the changes,
it's going to be in this section here. So what we're going to do
is we're going to make this new folder, and we're going to do
copy dash R. Next, Jas and ft marketplace FCC into next Jas
NFT. Marketplace, the graph FCC. So we're going to copy
recursively, everything that's inside that folder we just
created into this new one th
at we're going to make a lot of
adjustments to. And this might take a little bit of time to run
because we've got a lot of stuff in this folder. And Alright,
once we've done that, we can cd into this next Jas and ft
marketplace, the graph FCC and do code period and open this up
in a new code editor. Or as always, you can do File Open
folder to open it like that. Now that we're in here, we're going
to learn how to do this exact same project, instead of using
mirallas. But using the graph, one of
the things that we're
first going to do is we're actually going to deploy our
contracts to rink B. So we're going to grab our marketplace,
that soul, pull this over, we're going to grab our hard hat
marketplace dot soul project, pull it over. And first, we're
going to run our deploy script on rink v. So hopefully, you've
got all your deploy stuff set up correctly, so that all the
arguments can go through correctly for rink v as well. So
we're gonna go ahead and run this yarn, hard hat deploy das
h
dash network Rinkeby. And to make sure that it's going to
work for Rinkeby. Let's check our hard hat config. Okay for
networks, looks like I have my Rigby stuff in here for
networks. Okay, I've got my RPC URL, which I'm getting from my
environment variables. If I have a private key, I have a private
key, which I'm also getting from environment variables, I have a
chain ID block confirmations and save deployments. So let's go
ahead and run this. Now we're going to go ahead and be
deploying the
NFT marketplace to the Rigby network. And our
deploy script also has an ID some verification. So we can
verify this as well, you can't really follow along with this
section without deploying a marketplace to rink be here. So
deploy to rank B, or whatever test net is recommended in this
lesson 15 section, and go from there. Alright, great, we've
deployed our NFC marketplace. Now we're gonna go ahead and
deploy our basic NFT. And once these are done deploying, we can
start updating our front end c
ode to work with the graph
instead of mirallas. Okay, we've verified everything to which
looks great. Now we're going to verify our basic NFT. And we've
have it verified. Awesome. So now we have an NFT marketplace
deployed to ring P, and a basic NFT deployed to rink B. And both
of them are verified. So be sure to take note of those, because
we're going to need them when we're moving over to our the
graph section. So now let's grab our code editor titled next Jas
and ft marketplace, the graph. An
d let's get going. Now, we
just deployed that to rink B. But we forgot to add some code
in here to update our network mapping. However, if we go back
and we open back up code dot dot, next Jas NFT marketplace,
FCC, in the network mapping in here will now have a new entry
for rink B, if we did it correctly, let's copy this
network mapping and paste it over in our network, our next GS
and ft marketplace, the graph section, or you can just
manually add your new entry. Right. So you want to add
netw
ork ID for network marketplace. And the address of
that network marketplace that you just deployed, is we're
going to delete this Cloud Functions bit. Goodbye, because
since we're not working with the server anymore, there's not
gonna be any cloud functions or any back end to run. So we're
gonna move that to trash goodbye. Next, we don't need FRP
anymore, because we're not going to be connecting our local
blockchain to the graph, we're only going to be working with
the test net here. So let's go
ahead and delete that. Goodbye,
what's next in our pages, and our app dot j s. Right now we're
connecting to a Morales provider like this, we're going to switch
this back to initiate allies on Mount, this is going to go back
to being false, we're no longer going to connect to our Morales
database like this, we're just going to use the hooks again.
Now with that the only thing that's going to change is our
index.js. Right now, in our index.js, we're getting our list
of NF TS from our Morales que
ry. So we're going to change this,
let's update our readme. So we're gonna say instead of
reading the events from ralis, we will first off we're going to
index them with the graph. And then we're going to read from
the graph. So the question is, what is the graph. So the graph is going to be a
decentralized layer for storing event data. So there are all
these blockchains and all these different storage networks. And
the graph is and the graph is a network of different nodes that
read from blockc
hains and index this data, and it exposes an API
for us to call we can read that data. Rather than just myself.
We actually have nattered Abbott, who can explain it a lot
better than I can take it away. And at. First, I'd like to thank Patrick
for creating such a wonderful educational resource and
inviting me to be a part of it. My name is Nadia dabit. I'm a
developer relations engineer working with the graph. The
graph is an indexing and querying protocol for
decentralized networks like Aetheri
um IPFS, dozens of other
EVM compatible networks, as well as near and then the future
cosmos and Solana. Using the graph developers can build and
publish open API's called subgraphs that they can then use
in their applications to enable better querying capabilities of
data stored on these networks, including features like
filtering, sorting, relational data, and full stack search.
subgraphs live in between the blockchain and the UI providing
an important piece of software infrastructure, a flexi
ble
performant and decentralized API layer, and the traditional tech
stack databases, servers and API's query filter, sort
paginate group and join data before it's returned to an
application, usually via some type of HTTP request. These
types of data transformations are not possible when reading
data directly from Aetherium or other blockchains. Before the
graph teams had to develop and operate proprietary indexing
servers. This required significant engineering and
hardware resources and broke t
he important security principles
required for decentralization, how we interact with and build
on top of blockchains it's much different than what we are used
to in the traditional tech stack. In a blockchain data
isn't stored in a format that can be easily or efficiently
consumed, or retrieved directly from other applications or front
ends. The problem is that you need to have the data indexed
and organized for efficient retrieval. Traditionally, that's
the work that databases and web servers d
o in this centralized
tech stack. But that in indexing layer was missing in the web
three stack. Let's take a look at a couple of other examples of
indexing in the real world. Search engines like Google crawl
the internet indexing relevant data making it available for
users to search via their web interface and other API's.
Without this indexing layer, it'd be hard for us to know
where and how to find relevant information across the web.
Another similar analogy is a library. Using an indexing
sy
stem like the Dewey Decimal System, we know where to find
the book that we're looking for without having to go through
book by book looking throughout the entire library. The graph
serves over 2 billion queries per day to many different types
of web three applications, including apps in the defy
gaming and FC space. Before we dig into any code, let's take a
look at how to build a sub graph. To get started, you would
go to the graph.com and create a new sub graph and the graph user
interface. You
would then use the graph CLI to scaffold out an
empty sub graph boilerplate that you can then update with your
own contract information. In your subgraph configuration, you
would define things like your data model, the network, the
contract addresses and other configurations that are specific
to the data that you would like to index. For our data model, we
use Graph QL, schema definition language defining top level
types as well as fields within those types. When we're ready to
deploy our subgr
aph, so we can begin testing it out and using
it in our application, we can use the graph CLI running the
deploy command. Once the subgraph is deployed and the
data begins to be indexed, we can start testing it out using
the graphical interface directly in the graph dashboard. When
we're ready to start queering, our sub graph from our
application, we can use the API URL that's been given to us by
the graph along with any Graph QL query. If you'd like to learn
more about the graph, check out the
graph.com as well as Graph
Protocol, Twitter, the docs at the graph.com/docs, or our
Discord at the graph.com/discord Thanks matter. And now that we
have a better idea of what the graph is, we can actually start
building with it. Now if we were to try to run this app as it is,
it obviously would fail, right, because index.js, right now is
reading from mirallas instead of from the graph. So like it says
in our readme, first thing we're going to need to do is we're
gonna need to index from the gra
ph, and then we can just this
project to read from the graph. So let's go ahead and learn how
to build our sub graph. In order for us to tell the graph network
to start indexing the events from our contract, we're gonna
go to graph.com, we're gonna go to products. And we're going to
go to sub graph studio, if you go to products, the first thing
you see is the Graph Explorer, these are already existing sub
graphs. And if you go through here, you'll see a lot of
incredibly popular decentralized pr
otocols all have different sub
graphs, hosted service is going to get discontinued at some
point. So we're going to skip there. So let's go to sub graph
studio, this is going to help enable us to create a sub graph
for other nodes to start indexing our events. So we're
gonna go ahead and connect our wallet with meta mask. And I'm
going to choose account one here, next connect, and we're
going to want to switch off of Hardhead. Local, over to the
Rinkeby test network. And I'll go to Account one h
ere. And
we're going to get a signature request from the graph summary
to the website that we just built. The subgraph website has
some signings functionality with a database on the back end. So
we're seeing in real life, exactly the methods that we just
used. So instead of signing with Morales, they're just have their
own custom sign in here. So we're gonna go ahead and sign in
so that the graph website knows that it can interact with us.
And we can go ahead and enter our email if we want. I'm
gonna
go ahead and skip. And you'll even get a little notification
here saying only subgraphs indexing Aetherium or main net
or Rigby can be created in subgraph studio. So let's go
ahead and create a subgraph. So we're going to pick a cerium
Rigby and the subgraph name is going to be NFT marketplace.
We'll go ahead and hit Continue. Now, this is going to be our
dashboard for creating our sub graph. And there's a ton of
instructions over here and documentation that we can view
to get started, I'm
going to add this documentation to the GitHub
repo associated with this course. So this NFT marketplace
sub graph is going to need its own git repository itself. So
what we're going to do back in here is we're going to CD down,
we're going to make a new directory, and we're going to
call it graph. And if T marketplace, FCC, we're going to
open that up as well. So code graph NFT, marketplace, FCC, or
file open graft code marketplace FCC, and in this window, we're
going to build our subgraph. Loc
ally, we're going to build
our subgraph and push it up to the subgraph studio. And in
here, there's a whole bunch of instructions that we can follow
along with to go ahead and install. The first thing that we
need to do is install the graph command line interface. And we
want to install this globally. So I'm going to copy this part
here. Come back to my terminal Oh, yarn, global ad and paste
that in. It's this command line that's going to help us build a
graph and build instructions for the grap
h to actually start
indexing our events. Now that we've installed the globally, we
can initialize our graph code. So we can copy this line. And
we'll run graph a knit dash dash studio NFT marketplace and hit
enter. The protocol is going to be Aetherium, or subgraph. Slug
will be NFT. marketplace. We want the directory to be here,
but it's saying directory already exists. So we'll just
give it NFT marketplace. And then we'll move it after this.
And this is on the Rinkeby. Test net. And now we wan
t to give it
the contract address. So we want to grab that address that we
just deployed. And it should be now in our network mapping. So
we're going to grab that contract address that we just
deployed, and paste it in here. Like so. And since we've already
verified on ether scan, it automatically grabs the ABI for
us from ether scan, we'll give it the contract name, which is
the NFT marketplace. And it's going to give us a whole bunch
of boilerplate code in this NFT marketplace directory. And t
his
might take a couple of minutes. Alright, awesome. So now that
it's done, we see subgraph NFT marketplace created in NFT
marketplace. Now, I don't want it to be in the seller folder.
So what I'm going to do is I'm going to move it down a
directory, but you can leave it where it is, if you want, I'm
going to say move, I'm going to say move NFT marketplace, star
to dot slash. Now everything inside of NFT marketplace will
be in this current directory. And then if the marketplace will
be empty. S
o with that, I'm gonna go ahead and delete. And
if the marketplace. Alright, great. Now let me walk you
through exactly what's going on here and what these files are
that we just created. So our first folder is going to be
API's. In order for the graph to index and read our contract
index our events, it's going to need the ABI of our events. So
we've got the ABI of our entire marketplace, from ether scan, if
we didn't verify an ether scan, we can just create this API
folder ourself and add the N
FT marketplace JSON in here. Now we
have this generated folder. This is an auto generated file. He
even says at the top, do not edit this file directly. You can
kind of think of this as the build folder, or where we
compile graph code. Node modules, of course, is going to
be node modules and dependencies. SRC is going to be
where we define and we tell the graph, how to actually map and
how to actually work with our contract. And it is a TypeScript
file. So for all of you who have been just doing
this in
JavaScript, I will have to teach you a little bit of TypeScript
just to get through this part. Then we have networks dot JSON,
which gives us all of our network information about which
networks, what are the addresses, and what are the
different contracts that we're going to be indexing, package
dot JSON, which of course is just a normal package dot JSON.
And it's got some graph scripts already built in schema dot
graph. QL is going to be our Graph QL schema. So this is also
going to be
how we tell the graph how to actually work with
our events and index our events. And if you're familiar, the
schema follows the Graph QL syntax. So if you've ever worked
with Graph QL, before, it's going to be the exact same way,
graph. QL is a query language for your API. And instead of
being kind of a relational database, it can query in a more
graph type way, I'm not going to go too deep into how it actually
works behind the scenes. But if you want to learn more, I'm
going to leave some docs
in the GitHub repository associated
with this course, the sub graph dot Yamo tells our subgraph how
to combine all the files together. So we have data
sources, data sources, where they're coming from different
addresses, different entities or events, the API's where to grab
our files from different event handlers, which we'll talk about
in a minute. And then the main file, which is going to be our
mapping.ts, we have a TS config, which is a configuration file
specific to TypeScript. And then of
course, we have our yarn dot
lock. With all this information with all this code, we are now
going to update all this code to tell the graph to start indexing
our events. So we can read our events from the graph in a
decentralized context, as opposed to from a centralized
database. And after we build everything, we're going to run
through this off and deploy code, which is to authenticate
ourselves, and then deploy our code to the graph to start
indexing. So without further ado, let's jump in. A
nd let's do
this. So one of the first things I'm going to do, I'm going to
add a highlighter for these dot Graph QL files. So in our
extensions, we're gonna look at Graph QL. And we're gonna stall
this Graph QL extension. Now that we've installed it, if we
go back to schema dot graph, QL, we've got it with some colors
now, which is exciting. So this schema dot Graph QL is going to
define what entities we have in our contract if we were to be
analogous to mirallas. These are going to be how we're
going to
define what our tables are going to look like. And these are
going to be our events. plus that active item table that we
created. So we're not going to have an example entity though,
we're going to have a type active item. And this is going
to be an add entity. So these are the different types we have
in our graph that we can actually query on, our main
thing that we're going to want to query on is same as before,
it's going to be our active items. And then inside of our
active items,
we're going to tell the graph, what parameters
each one of these active item types has, well, it's going to
have an ID of type ID. So the variable is ID of type ID. And
you can read more about the different types in the graph
documentation. And this exclamation mark means it must
have an ID. So every active item needs to have and will have an
ID, we'll say there's going to be a buyer, which is going to be
an address, so that's going to be a byte and the buyer could be
blank, right? It could be t
he 0x 000, we're going to have all
of our active items have a 0000 dot.if. If no one has
bought yet, we're going to have a seller, which of course is
also going to be a bytes because it's going to be an address,
we're going to have an NFT address. So the address of the
NFT, which will also be bytes, we're going to have a token ID
which isn't going to be bytes, this is going to be of type big
int, and then we're going to have a price, which is also
going to be a big int. Now price we're going to
leave as not
required for price, we're just going to have price be nullable.
So price can be null. So we have an active item. Awesome. What
else are we going to need? What what other tables do we have in
Morales? Well, we're going to need type item listed. And this
is going to be at entity is going to need an ID, it'll have
a seller. Whenever an item gets listed, it's going to have a
seller right, which is going to be a bytes again. And if the
address the address of the NFT, which will also be b
ytes, the
token ID, which is going to be a big int, and then the price,
which will also be a big end, we're gonna need a type item
cancelled at entity which will have an ID seller an
NFT address. And a token ID will be a big end. And then last type
item bought will be in that entity is going to have an ID
ID. It's going to be a buyer bytes. And then of T address a bytes
and a token ID again, and then a price of beginnt. And with
Justice Information, we've defined what we want our
subgraph to kee
p track of wants to keep track of item bought
events, item, cancelled events, item listed events. And then
we're going to make this new active item table similar to
what we did with mirallas. And it's going to be some function
of these these other three events. And now we're done with
our schema dot graph. QL. Awesome. So what do we do now
we're going to need to tell our sub graph to actually listen for
these events. So what we can do. So what we want to do to tell it
how to listen for these eve
nts, is we're going to go to src
mapping.ts. It's this mapping.ts file that's going to tell our
subgraph how to actually map and how to actually store all the
event information that we have. If you look in it right now, it
might even give you kind of a sample event. So it says Export
function, handle item event. This is what Maya says right
now, it takes as an input parameter event item bot. So
this is saying whenever an item bot event occurs, do this handle
item bot function. So anytime item bo
t happens do this handle
item bot. And we're actually getting this item bot from our
generated NFT marketplace from some generated code. In the
graph. If we run graph code Gen. This graph code Gen command
grabs all the stuff we have in the schema dot Graph QL and puts
it in this generated file. Now that I've run graph code Gen,
you'll see in here there's an item bot class, you see there's
a schema, that type script. And actually we can even find that
new active item class that we created in our
schema. So
anytime you update schema dot Graph QL, you're always going to
want to run graph code Gen, so you can update those types. And
if this failed, it means that you messed up something in your
schema dot Graph QL. Now in our mapping.ts, we're actually
importing item bought item cancelled item listed from
generated and if the marketplace and if the marketplace from our
generated code. These are going to be our events. And we're not
going to need to do anything with the NFT marketplace. We'r
e
just going to need our event information. For now. Let's go
ahead and just delete everything inside our handle item bot or
whatever sample is given to you. So we're importing a or events
from our generated code. And then we have this line here,
which we're going to change in a second. So again, we have these
three functions handle item bought, handle item canceled and
handle item listed. Whenever we get an item bought event, we're
going to do this function. Whenever we get an item canceled
eve
nt, we're going to do this function. And all this code is
defined in our sub graph dot Yamo. You can see the different
entities here in a bot item cancelled item listed, and the
event handlers. So it says, okay, anytime this specific
event gets fired with an index address, an index address index,
YouTube 36 in YouTube had six call handle item bought, which
again, we're getting in here handle item. But so that's
exactly how this works. So let's figure out what to do when an
item bought event trig
gers. And I think that item bought item
cancelled item lists it is a little confusing. So I like to
change this to item bought as item bought event item cancelled
as item cancelled event item listed as item listed event. So
I'm just changing the names of these three that are imported
from NFT marketplace. And now I'm going to change event item
bot to item bought event. Event item cancelled to item cancelled
event and event item listed to item listed event. Okay, great.
And we're just going to re
move this line for now. And we also
don't need this line at the top at the beginning, we will in a
minute, but we'll delete it for now. So here's our minimalistic
code here. So whenever we list an item, what do we need to do?
Well, we need to save that event in our in our graph. And then we
also need to update our active item, exactly as we did with
Morales. So first thing that we're going to need to do is
either get or create an item listed object. And something
that we need to know is that eac
h item needs a unique ID. And
we actually need to create that ID. So one of the first things
I'm going to do is going to create a function called Get ID
from event params. And it's going to take a token ID and
here's where TypeScript comes into play a little bit. In
TypeScript, we actually need to define the types of our
different parameters. So token ID is going to be a big int. And
we'll also take an NFT address, which will take in a type of
address. And we also need to say what return type or
function is
going to give, which we're going to return a string, we're going
to create an ID from event params. And it's just going to
be a combination of the token ID and the NFT address. The
combination of these two will give a unique ID for each one of
these types of event. So we're just gonna say return token ID,
and token ID has a function called to x string, I'm gonna
say plus, and if T address.to X string, like so. And big int an
address, we need to import from at Graph Protocol slash gr
aph
TX. It already imported big enough for me. So I'm just going
to add address in here is the two special types that come from
the graph. And then string is built into TypeScript. Now that
we have a way to get a special ID for each item in our function
here, we have to now either get or create a new item listed. Now
right now we have an item. But event we don't have an item
bought object. So the item bought object is going to be
what we save, the item bought event is just the raw event. So
we h
ave to create an item bought object from our item bought
event. And in TypeScript, these are going to be two different
types. So we have to import these item by objects. So those
actually get auto created from generated schema. In here we
have active item, we have item, we have an item bought class, we
have an item cancelled, etc. So we're gonna have to import those
types from there. So we can say, import, item listed, comma,
active item, item bought an item cancelled from dot slash,
generated s
lash schema. Let's go ahead and get or create an item
bot object. So we'll say let item bot equals item bot dot
load. And this is how we load an item we load its unique ID by
calling this get ID from event params dot load, get ID from
event params. And we can pass event dot params. That token ID because an item bot event is
going to have a token ID and event that params.nf T address.
Now I know we probably should have done handle item listed
first but we're doing item bot first. Since we're buyi
ng an
item, we probably will also have an active item as well, right?
We haven't made yet an item listed. But this is going to be
similar to what we did with Morales. So we know that every
time we live Certain items will also list an active item. So
we'll say let active item equals active item that load. And we're
going to do this exact same thing. So I'm just going to copy
paste it into here. And even though these are going to have
the exact same ID, it doesn't matter because they're the same
I
D across different types. Now we're gonna say, if there is no
item bot, we'll say item bot equals a new item bot object.
And we'll give it an ID, which is going to be exactly our ID
giving parameter here, get ID from events, and pass that
there. So we're going to create a new item bought here. And now
we're going to update all its parameters. So back in our
schema dot graph, QL. An item bought has an ID, buyer address,
token ID and price. So we're gonna say, item bought, that
buyer equals event
dot params dot buyer, item bought.nf T
address equals event.params.nf T address, item bot, dot token ID
equals event dot params dot token ID. And that looks good.
And our active item will be from item listed, and item listed
should give it all these parameters, except for it won't
have a buyer. So we just need to update the buyer on our active
item. So we'll say active item, dot buyer, we'll do a little
exclamation mark. This is some TypeScript stuff, saying we will
have an active item. Don't wo
rry too much about it if you're
unfamiliar with TypeScript. And we'll say that equals event dot
params dot buyer. And now similar to Morales, we're gonna
do item bot dot save and active item with an exclamation mark
again, that save and this is how we're going to save this item
bought event as an object in our the Graph Protocol. And also,
we're going to update our active item. So this is our full
function of handle item bought. Whenever somebody buys an item,
we update a new item bought object,
and we update our active
item to be a new buyer, we're not going to delete it from our
active items list, we're just going to update it with a new
buyer. And we'll just say if it has a buyer, that means it's
been bought. If it doesn't have a buyer, that means it's still
on the market. Awesome. So now that we've done our handle item
bot, let's now do our handle item listed, which will
hopefully make our handle item bot a little bit easier to
understand. So for handle item listed, we're going to
do the
same piece here. So we're going to say let item listed equals
item listed dot load. And we're gonna do the exact same thing as
what we did for all these will do get ID from event params.
Like so, get a different event params. And we're gonna say let
active item. And this line is going to be exactly the same as
up here. So I'm just going to copy paste. So we're saying,
okay, great, grab our item listed, and grab our active
item, see if those objects already exist. I will say if
there is no
item listed, which there shouldn't be, we'll go
ahead and create a new one, we'll say item listed equals new item listed. And its
ID is going to be from this function that we created are
unique IDs. Now, unlike what we did above, we're also going to
say, if there is no active item, then we're going to create a new
active item, right? Because we're listing an item, it
shouldn't be an active item. Now this functionality is going to
make a lot more sense here, right? Because if we're updating
the
price of an item, active item will already exist. If it's
a brand new listing, though, active item will not exist. So
we'll say okay, if it doesn't exist, okay, that means it's a
brand new listing, we'll say item listed equals new item
listed, and then we'll give it an ID. That's the same ID
methodology, paste that in. So now all we got to do is update
these new objects. So I will say item listed that seller equals
event dot params dot seller. And I'm just going to copy paste
this line. Because
this is just gonna be active item dot seller
now. Oops, see me and then it'll make this active item. active
item instead of item listed. And what else comes with item
listed? Well, let's go to the schema item listed has an NFT
address, token ID and a price. So okay, so let's add those.
item listed dot NFT address equals event.params.nf T
address. Copy paste this line because it's gonna be the be the
exact same for active item. Now item listed that token ID equals
event dot params dot token ID. C
opy paste this line. Same thing
for active item. Item listed dot price equals event dot params
dot At price, copy, paste this line for active item. And then
we just say those two. So item listed that save active item.
Save. So in our protocol here, if it's already been, if there
already is an active item, then we just go ahead and we get that
active item. This would be for a listing that we're updating. If
not, we make a new one. We update it with whatever came in
through the event. And then we
save it to our graph protocol.
Okay, perfect. Now we only have one left, item cancelled. So
let's figure out how to do item cancelled, it's going to look
really similar to item bought. So we'll say let item cancelled
equals item cancelled that load. Again, we're going to do this
exact same Id get our that we're doing for everything, we'll say
let active item equals, and I'm going to zoom out a little bit
less active item equals active item dot load. Or do this exact
same thing here. Boom. And th
en we're gonna say, if not item
cancelled, which there shouldn't be because this should be the
only item canceled event here. We'll say item cancelled, equals
new item cancelled. And we're gonna give it an ID using the
same ID methodology we've been using. Now this is going to look
a little bit different. We're gonna say item canceled, that
seller equals event dot params dot seller. So far, so good.
Item canceled dot NFT address equals event dot params dot NFT
Address item canceled dot token ID
equals event dot params dot
token ID. And then finally, we are going to change the active
item a little bit different than what we've seen. active item
exclamation mark again, ignore if you're confused by that dot
buyer. And we're going to update the buyer to equal address dot
from string. We're going to give it what's
called the Dead address. And that's this right here.
0x 1-234-567-8910 1112 1314 producing 718 1920 21 to two to
three to 45 to 67 to 8930 3130 230-334-3536 zeros
lowercase d, upp
ercase E, lowercase A uppercase D, this is
known as the dead address. And this is how we're going to
decide if an item is still on the marketplace or not. Or if an
item has been bought or not. If we have the dead address as its
buyer, that's going to mean that the item has been cancelled. And
that's how we are going to be able to tell that an item is on
the marketplace or not dead address means it's been
cancelled. An empty address, which is what will happen for
handle item listed means it's on
the market. And an actual real
address means that it's actually been bought by somebody. So the
way we can tell if it's on the market is it's 0x 0000000.
Because the data address is obviously going to be different
than than all zeros. The data address is a commonly used
address kind of as a burner address that nobody owns, then
we can just say, item cancelled that save an active item that
say active item, exclamation mark dot save and perfect. Our
mapping file is now completed. We now have three
different
functions to define how to handle when items are bought
events, how to handle when items are cancelled event and item
listed events. And if you're confused, remember all the code
for this is available on this the graph section of my GitHub.
So you can just follow along with the code here, if you ever
get lost or need help. And with that, we're almost ready to tell
our subgraph to start listening to our contracts, there's just
at least one more thing that we want to do. So in our sub g
raph
dot Yamo, we'll see source address blah, blah, blah, this
is telling us to start indexing events since the beginning of
Aetherium. Now we don't really want it to do that, because it
will take a really long time, we want to tell our subgraph Hey,
you don't just start from beginning of time, you just need
to start from right before our contract was deployed. So we can
add what's called our start block to tell it what block
number to start deploying. Now if we have our address, which we
do rig
ht here, we can copy it, we can paste it on to the rink
fee, ether scan, paste it in here, or really any block
Explorer, and we'll see what block number our contract was
deployed. And it looks like it was this block. So I'm going to
copy that address, go back to my code and say starting block,
it's going to be right here. Starting block is going to be
when it was deployed, minus one. So we're gonna go right before
we deployed our contract, we're going to start reading any event
that is indexed f
rom it. Now if you just deployed this, it might
not have any events in it at all, which we're going to fix in
just a minute so don't worry, but with that all the
instructions for how to build Our sub graphs are ready to be
deployed to the graph, start our indexing and start working with
our instructions in a decentralized context. So what
we can do now is back in the graph, we actually have the auth
and deploy code right here, we can copy this, this graph auth
dash dash studio, which is our depl
oy key on how to deploy. And
we can run this in our code editor. So we're going to paste
that in here, graph off dash studio, hit enter. And we're
gonna say deploy key set for the graph. So this is just setting
us up so that whenever we push our code, it's going to push it
to this sub graph configuration that we've made on their site to
help us deploy automatically. Now we can do, we don't need to
enter the subgraph, because we've already moved stuff down,
is we'll build the sub graph, we'll run
graph code Gen, we can
just run graph kojem, which again, is just going to
make sure our schema dot Graph QL looks good. And then we're
going to run our graph build. And this graph build command is
going to compile and run all of our sub graph stuff, everything
in mapping that JSON all our generated code. And it's going
to put this into a real build folder, the generated folder has
kind of like a pseudo build folder. And then we have a real
build folder. So the generated is just to build some t
ypings
for our TypeScript. And it's this build folder, this real
build folder, is what we're going to actually be deploying
and sending to the graph. And we can actually deploy our
subgraph. Now, with graph deploy dash s studio. And if the
marketplace, which we're going to run right now, now is going
to give us a version label option, which we're going to
give it the 0.0 point one, since this is our first version, and
it goes ahead and starts deploying it to the graph, we
also get to upload our
sub graph to IPFS. And we have a little
hash right here for IPFS for a subgraph that we could look at.
But now if you're successful, we now have this build completed
thing. And we have these sub graph endpoints for queries and
subscriptions. So we can actually start querying and
subscribing to our sub graph. But if we go back to our site
here now and we hit refresh, we can now see status deployed, we
can see that we're sinking. And now we have some nodes that are
listening for our events to be e
mitted here, which is
incredibly exciting. We can go to logs to see if anything went
wrong. And right now it's just indexing, it's listening. It's
going through all the different blocks in the blockchain,
listening for our events. And then we have a playground here
where we can run some queries to see different events and the
different responses from our Graph QL, which right now, it's
totally blank, because we haven't done anything yet. So
once again, let's pull up our hard hat and ft marketpla
ce
code, or open it up in a new terminal, whatever you want to
do. And let's go ahead, and let's run our mminton list
script. But for rink B, so we'll do yarn, hard hat, run scripts,
mint, and list item dash dash network Rinkeby. So we're going
to mint an NF T, right, this is going to be to transaction, so
we're gonna have to wait a little bit, and then we're going
to approve the NFT. And then we're going to list it on our
marketplace. And once it's listed, it's going to emit an
item listed even
t. And we should see now we have an active item
and an item listed data in our Graph QL. Now you can learn more
about now what you see here on the left hand side is what's
known as a Graph QL query. Now, we're not going to go over how
to do these, but I'm going to leave some links in the GitHub
repository. If you want to learn how to do more of these queries,
these are going to be similar to what we saw with Morales. But
instead of them being kind of regular table lookups. They're
in Graph QL sy
ntax, and the results of our query, end up
being over here, we can see more information about our schema all
the way to the right over here. But if we look at our code, now,
we've made the NFT approved it we've listed it on our
marketplace. So now if we go back to the graph, and we run
this query, and we do a little refresh on our NFT marketplace,
we might have to wait a few minutes for the graph to index
these new blocks. But in our playground, we should see the
show up as a query. If you don't
automatically get these you can
pause the video to write these into your Graph QL playground,
right? So after a few minutes, refresh on ether scan, I can see
that list item transaction has gone through. This means we've
emitted a new event. So if I come back to my playground, and
I hit play here, oh my goodness, we can see we have active items.
And we see we have item listed. This is fantastic. That means in
a decentralized context, we have a off chain service that is
indexing our events for ou
r contract so that we can update
our front end and we can update people in a decentralized way.
This is so exciting. Awesome, so now that we have
this all set up, we can finally go back to our next JS project
or next Jas NFT marketplace the graph FCC. The reason we did all
this is because right Now in our code base for our next Jas NFT
Marketplace application, we're reading from a Morales database,
which we're not going to do anymore. Instead of reading from
a Morales database, we're going to re
ad from the graph, let's go
ahead and learn how to update our code our index dot j, s to
read from the graph instead. So to highlight this, to show this,
we're actually going to create a new page, a new file, and we're
just going to call it graph, example, dot j, s. And we're
just gonna make this a really minimalistic page to show you
how to do a graph query. So similar to index dot j, s, we're
gonna do export default graph example. It's going to be a
function, or default function, graph example
. And we're gonna
use this tool called Apollo client, copy, paste it over. And
we're going to add it with yarn, add dash dash dev at a P O ll O,
slash, client, or excuse me, not Dev, because we do need this on
the front end. So yarn add at Apollo slash client. Oh, sorry,
we also need to do yarn, add Graph QL, we need to add both of
those. And it's this Apollo slash client, which is how we're
going to make queries to our newly created graph. QL. So
we're gonna say import the use query hook, from
this package we
just installed from at a P, O, ll o slash client. And we'll
also import G QL. So to create a new query, we'll say const, get
active item equals g QL. And we'll add this backtick here.
And we'll add all of our Graph QL stuff in here, this is gonna
be equivalent to this, this is Graph QL. Syntax. And we're
gonna be putting this Graph QL syntax into our code base here.
But we only want to get the active items. So how are we
going to get the active items? Well, we can actually build
it
over here on our playground first, and then add it to our
code base. So we'll say we want to grab the active items, we'll
grab the first five, and then we'll say where the buyer is
going to be 0x 12345678. We're saying where the buyer is empty.
So we're grabbing the active items where there is no buyer,
and then we're going to do and then we're going to get the ID,
the buyer, the seller, the NFT address, token, ID and the price
from that. And if we hit run, we get our active item here. So we
see here, the buyer is this exact 0x 12345678 Because it
gets defaulted to zero when there is no buyer. And this is
why for item cancelled, we updated the buyer to be that
dead address. So if it's bought, it won't be active anymore. And
if it's cancelled, it also won't be active anymore. Now we can
copy this query and paste it into our code. And now we have a
Graph QL query that we can use for our graph example. Now in
our graph example, we'll use this query with this use query
hook. So in expor
t default function, graph example, we can
say const. And it comes with a whole bunch of stuff like
loading error, and then the data returned equals, use query. And
then we'll just pass it this Get active item, or get active
items, let's put an S on it, get active items. And then we can
just return return a little div. And we'll say hi in the div, and
then we'll just do console dot log data. And then now we'll go
back to our app.js, where we're wrapping everything in a Morales
provider, we also n
eed to wrap everything in an Apollo
provider. And we need to initialize it kind of similar to
how we initialize connecting to our morale server, but we're
going to initialize connecting to our Graph QL. So we're gonna
say import a P O ll o provider, a P O ll o client, and in mem
ory, cache from at a P O ll o slash client, like that, and
then we'll have to initialize this so we can delete the morale
stuff. We'll initialize this client by saying const client
equals new a P O LL. O client, and we'l
l give it the parameters
here. And you can find this all in the Apollo client
documentation will say there's going to be a cache to help when
we do refreshes and stuff we'll say new in memory cache, and we'll say
comma URI, a que where we're going to be connecting. And this
is where we're going to add the API for our sub graph. So if we
go back to details, we can see temporary query URL and this is
a rate limited temporary query, because this is just a test net,
and we're going to copy this, go
back to our code. And we're just
going to paste it in here. So whatever you have for temporary
query URL, and your sub graph studio, that's where you're
gonna paste in here. Now, this client tells our Graph QL, where
it should be making those queries. And we're gonna make it
to here. Now, this starts with HTTPS. So is this centralized?
Yes, because we are directly calling the graph website.
However, all the data is still gonna be stored in this
decentralized graph indexer. And kind of similar to
what we did
with IPFS, we're doing this kind of as a gateway to make it a lot
easier for us to connect and read the data from the graph
studio. However, in the future, as more protocols and more
browsers adopt the graph, and IPFS, this will become a lot
easier inside of our Morales provider, but outside of our
notifications provider, we're gonna say a P O ll o provider,
and then client equals client. And then we're going to copy the
closing tag, and put it around the notification provider and
p
ress Save. Now I'm gonna save our front end, now we're going
to try to run our front end. And we're gonna have to change some
stuff in here because it's going to freak out. So we're gonna run
yarn Dev, for new front end. So we're gonna go to our localhost
3000. And it's going to totally freak out, because we still have
some morale stuff in here. And that's totally okay. We, of
course, we don't need a hard hat Node running. Because we're on a
test net, we don't need to be synched with Morales, we
don't
need to be doing any of that stuff. Because we're working
with a test net, right now, it's actually not freaking out, which
is great. But it shows obviously nothing for recently listed. So
what we're going to do now is we're gonna do slash, graph
example, and hit enter. And we can see Hi, show up. But if we
go to inspect, and we go to console, a console here, we can
see an object here of active items, which is returned from
the graph with buyer ID, NFT, address price and all this stuff
in
the air. This is fantastic. So okay, okay. Okay. So all we
have to do now is we just have to update. So let's go ahead,
we'll kill our server. For now, all we have to do is update,
instead of use mirallas query, we're going to delete this. And
we're just going to query from Apollo query from our Graph QL.
And everything else stays the same, right? Because our NFT
box, and all the rest of the code that we worked with, will
still work exactly the same. So first, we just got to get our
address. So
we're gonna say import, same as Sell, sell NFT,
import the network mapping, so I'm actually just going to copy
it. So we're going to import network mapping from constants,
network mapping dot JSON. And now we can say const.
Marketplace, address equals, and we're going to get the exact
same way we did this and sell NFT to, so we're going to grab
this line getting the chain string, we'll paste that in
here, which means we're going to need to get chain ID from user
Morales. And then we're gonna ge
t the marketplace address like
this. So we're going to copy that line, paste it in here.
Okay, great. Now we have the marketplace address. So now
we're just going to do const. Loading error data, which we can
do listed. And if T is again, equals, use query, and we can do
that get active items. So what I'm going to do is I'm going to
create a new file and constants, we're going to pass that get
active items thing in their new file, we're just going to call
it sub graph queries, dot j, s. And then
here, we're gonna say
import G QL. From at a Apollo slash client. And we'll say
const, get active items, equals g QL backtick. And exactly what
we did in that great, let's go back to the graph example, we'll
just copy that whole line actually pasted in here. And now
we'll just do export default, get active items. And we'll
import this query from sub graph queries into our index dot j s.
So we'll say import, get active queries are skimming apt get
active items from dot dot slash constants slash
sub graph
queries. And now in our use query will pass Get active
items, which should return are listed NF Ts. So now, we'll
change this from fetching listed NF Ts to loading, loading, or we
don't have listed NF Ts. So if it's loading or we don't have
listen if TS then do loading, otherwise, we're going to do
another mapping, but the return of the Graph QL is going to be a
little bit different. So instead of listing MTS dot map, it's
going to be listed NF T's dot AQ two have items, dot map and f
t,
and then we're going to get price. And if the address token
ID, we're not going to get marketplace address, but we'll
get seller from NFT. And it's not going to be returned with
attributes. So we can just do console dot log NFT instead. And
then we just pass all that stuff normally to, to that NFT box
exactly the way that we did before. So really, we're all
we're doing is we're swapping out the query methodology here,
price is going to be from price price is going to be from the
query, and ft
address is going to be from the query token ID
from the query marketplace address, that's also gonna be
slightly different, we're going to get that from our own config
seller is going to be from the query, and then the key is going
to be from the query as well. So now if we save that, if we
restart our website, with yarn Dev, and we go back to our
homepage, we should see everything exactly the same,
except for the images being pulled from the graph, instead
of being pulled from her Alice. Let's
go back to our front end,
we'll give it a nice refresh, close out the console. And we'll
go to our homepage. And I forgot to do use query. So let's import
use query, import, use query from at a Paulo slash client.
Let's save and let's go back and give that a refresh. And oh, my
goodness, we now have updated to get our events from a
decentralized data structure. That's freaking awesome. Now
let's talk about hosting this real quickly, we are using the
image tag in here in our NFT box, we are usin
g the image tag
in here from next Jas, which comes with some pre processing.
So it's a little hard to use on IPFS. So we would need to update
the way we do images in order to host this on IPFS. But we still
can do that. Some other options we have actually are Morales, we
can actually even host our apps on Morales, if we want. We can
also use things like for cell or Netlify, or etc, or really any
other traditional centralized hosting service. Now, if you
want to, I challenge you to update this co
de to make it be
able to be hosted on IPFS. So that you'll have an end to end
decentralized. And if the marketplace first wanted to make
a PR to this code, so that it can be successfully hosted
completely end to end on IPFS wins and empty for me. But wow.
And with that we are done with less than 15. This is an
absolutely monstrous accomplishment. And if you
finish this, if you've understood everything, if you've
gotten through everything so far, you should feel incredibly
proud of yourself. Beca
use this is our last full stack section,
our front end section and you're a full stack monster at this
point. huge congratulations, you should be super, super, super
proud. And definitely take a break, go get a coffee, and get
ready for the final stretch of lessons 1617 and 18. Those are
gonna go by a little bit quicker. I'm very excited for
you take a break, and I'll see you there. Right, welcome to Lesson Six
tene, where we're going to be going into even more low level
code here. The hard hat
upgrades of course, per usual, our entire
GitHub repository is located here. And additionally, we have
an optional video that you can watch. If you want to learn
more, we're actually going to watch a slice of that video that
explains upgradable smart contracts. So let's jump in. Now
I'm editing this video much later after I filmed that, hence
why I have a beard so I'll be jumping in from time to time
updating some of the sections. When deploying your smart
contracts on chain. We all know that th
ose smart contracts are
immutable or unchangeable. But what if I told you that they
were immutable? Well, technically, that wouldn't
be correct. However, smart contracts actually can change
all the time. When people transfer tokens when people
stake in a contract or really do any type of functionality. Those
smart contracts have to update their balances and update their
mappings and update their variables to reflect this. The
reason that they're immutable is that the logic itself never
changes a
nd will be on chain like that forever. So
technically, yes, once they are deployed, they are immutable.
And this is actually one of the major benefits of smart
contracts in the first place that nobody can tamper with or
screw with our smart contracts once we deploy them. However,
this can be an issue if, for example, we want to upgrade our
smart contract or protocol to do more things, or want to fix some
glaring bug or issue that we have. Now even though we can't
change the specific code that's
been deployed to an address, we
can actually do a lot more than you think. And in this video,
we're going to explain the different methodologies behind
upgrading your smart contracts. And then we're going to show you
how to do it with hard hat and open Zeppelin huge shout out to
a lot of open Zeppelin and trilobites articles that helped
me put this video together and a number of other sources as well
links in the description. So let's get to it. Now at first
glance, you might be thinking, if you
can upgrade your smart
contracts, then they're not really immutable that in a way
you'd be right. So when explaining kind of the different
philosophies and patterns that we can use here, we do need to
keep Behind the philosophies and decentralization implications
that each one of these patterns have, as they do all have
different advantages and disadvantages. And yes, some of
the disadvantages here are going to affect the centrality. So we
need to keep that in mind. And this is why it's so impo
rtant
that before you go ahead and jump in and start deploying
upgradable smart contracts, you understand the trade offs. So
we're going to talk about three different ways to upgrade your
smart contracts. The first one being the not really slash
Paramor ties way to upgrade your smart contracts, the social
migration method, and then the method that you probably have
heard about, which is proxies, which have a ton of
subcategories, like metamorphic contracts, transparent
upgradable, proxies, and u
niversal upgradable proxies. So
let's talk about the not really upgrading method, or the
parameterization method, or whatever you want to call it.
This is the simplest way to think about upgrading your smart
contracts. And it really isn't upgrading our smart contracts.
Because we can't really change the logic of the smart contract,
whatever logic that we've written, is there, we also can
add new storage or state variables. So this is really not
really upgrading. But it is something to think abou
t
upgrades is just parameterizing, everything, whatever logic that
we've deployed is there, and that's what we're interacting
with this function means we just have a whole bunch of setter
functions. And we can update certain parameters. Like maybe
we have a reward parameter that gives out a token at 1 percent, every
year or something like that. Maybe we have a setter function
that says, hey, update that to 2 percent, or update that to 4 percent. It's
just a setter function that changes some vari
able. Now, the
advantages here, obviously, this is really simple to implement.
The disadvantage is that if you didn't think of some logic or
some functionality, the first time you deployed their smart
contract, that's too bad, you're stuck with it, you can't update
the logic or really update anything with the
parameterization, aka, not really method. And the other
thing you have to think about is who the admins are, who has
access to these setter functions to these updating functions. If
it's a
single person, guess what, you have a centralized
smart contract. Now of course, you can add a governance
contract to be the admin contract of your protocol. And
that would be a decentralized way of doing this. So just keep
that in mind, you can do this method just need a governance
protocol to do so another example of this might be a
contract registry. And this is something actually that early
versions of ABA used before you call a function, you actually
check some contract registry that is upd
ated as a parameter
by somebody and you get routed to the contract, and you do your
call there. Again, this really doesn't allow us to have the
full functionality of upgrades here, you can argue that this
registry is a mix of one of the later versions. But for all
intents and purposes, this doesn't really give us that
flexibility that we want for upgrades. But some people might
even think that upgrading your smart contract is ruining the
decent reality. And one of the things that makes smart
con
tracts so potent is that they are immutable, and that this is
one of the benefits that they have. So there are some people
who think that you shouldn't add any customization or any
upgradability, you should deploy your contract. And then that's
it. trilobites has actually argued that if you deploy your
contract, knowing that it can't be changed later, you take a
little bit extra time, making sure you get everything right.
And there are often less security vulnerabilities because
you're just sett
ing it forgetting it and not looking at
it again. Now, if I wanted to upgrade a smart contract with
this philosophy in mind, the philosophy that I do want to
keep my smart contracts immutable, we can instead use
the social migration method, which I previously called the
Eat method. And now I think it's less funny. So we're just gonna
stick with social migration, the social eating method, or the
migration method is just when you deploy your new contract,
not connected to the old contract in any w
ay. And by
social convention, you tell everybody, hey, hey, this new
contract, this new one that we just deployed? Yeah, this is the
real one now. And it's just by convention of people migrating
over into using this new one, that the upgrade is done, hence
my slang name of social yeet. Because you use the first one
out of the way and move to the second one. I think I'm funny. Yeah, this
has the advantage of truly always saying, Hey, this is our
immutable smart contract. And this is our new one.
This is
really the truest definition of immutable, because since you
give it no way of being upgraded in place, than if somebody calls
that contract in 50,000 years in the future, it will respond
exactly the same. Another huge disadvantage here is that you
have to have a totally new contract address. So if you're
an ERC 20 token, for example, you have to go convince all the
exchanges to list your new contract address as the actual
address. Keep in mind that when we do this, we do have to move
th
e state of the first one over to the second one. So for
example, if you're an ERC token moving to a new version of that
ERC token, you do, you have to have a way to take all those
mappings from the first contract and move it to the second one.
Obviously, there are ways to do this, since everything is on
chain. But if you have a million transfer calls, I don't want to
have to write the script that updates everyone's balance and
figures out what everyone's balance is just so I can migrate
to my ne
w version of the contract. So there's a ton of
social convention work here to do. trilobites has actually
written a fantastic blog on upgrading from a v1 to v2 or
etc. With this eat methodology and they give a lot of steps for
moving your storage and your state variables over To the new
contract. So link in the description if you want to read
that. Now let's get to our big ticket item. So in order to have
a really robust upgrading mentality or philosophy, we need
to have some type of methodology
or framework that can update our
state, keep our contract address and allow us to update any type
of logic in our smart contracts in a easy way, which leads us to
our big ticket item is the proxies. What's our big ticket
item? proxies, proxies. Proxies are
the truest form of upgrades, since a user can keep
interacting with the protocols through these proxies, and not
even notice that anything changed or even got updated.
Now, these are also the places where you can screw up the
easiest. Proxies
use a lot of low level functionality. And the
main one being the delegate call functionality. Delegate call is
a low level function where the code in the target contract is
executed in the context of the calling contract. And message
dot sender and message dot value also don't change. So you
understand what delegate call means. Now, right? Great. And in
English, this means if I delegate call a function in
contract B from contract a, I will do contracts B's logic in
contract a. So if contract B
has a function that says hey, store
this value in a variable up top, I'm going to store that variable
in contract a, this is the powerhouse. And this combined
with the fallback function allows us to delegate all calls
through a proxy contract address to some other contract, this
means that I can have one proxy contract that will have the same
address forever. And I can just point and route people to the
correct implementation contract that has the logic, whenever I
want to upgrade, I just deploy
a new implementation contract and
point my proxy to that new implementation. Now, whenever a
user calls a function on the proxy contract, I'm going to
delegate call it to the new contract, I can just call an
admin only function on my proxy contract, let's call it upgrade
or something and I make all the contract calls go to this new
contract. When we're talking about proxies, there are four
pieces of terminology that we want to keep in mind. First is
the implementation contract. The implementati
on contract has all
of our logic and all the pieces of our protocol. Whenever we
upgrade, we actually launch a brand new implementation
contract, the proxy contract proxy points to which
implementation is the correct one. And routes everyone's calls
to the correct implementation contract, you can think the
proxy contracts sits on top of the implementations the user,
the user is going to be making contract and function calls
through the proxy contract. And then some type of admin, the
admin is th
e one who's going to decide when to upgrade and which
contract to point to. In this scenario, the other cool thing
about the proxy and delegate call is that all my storage
variables are going to be stored in the proxy contract and not in
the implementation contract. This way, when I upgrade to a
new logic contract, all of my data will stay on the proxy
contract. So whenever I want to update my logic, just point to a
new implementation contract, if I want to add a new storage
variable or a new ty
pe of storage, I just add it in my
logic contract and the proxy contract will pick it up. Now,
using proxies has a couple of gotchas. And we're gonna talk
about the gotchas. And then we're going to talk about the
different proxy contract methodologies, because yes,
there are many proxy contract methodologies as well. And this
is why trilobites doesn't really recommend using upgradable
proxies for your smart contracts. Because they're
fraught with a lot of these potential issues. Not to
mention,
again, you do still have some type of admin who's
going to be upgrading your smart contracts. Now, if this is a
governance protocol, then great, you're decentralized. But if
this is a single group or entity, then we have a problem.
The two biggest gotchas are storage, clashes, and function
selector clashes. Now, what does this mean? When we use delegate
call, remember, we do the logic of contract B inside contract a.
So if contract B says we need to set value to two, we go ahead
and set value to
what these smart contracts are actually
kind of dumb, we actually set the value of whatever is in the
same storage location on contract as contract B. So if
our contract looks like this, and we have two variables and
contract a, we're still going to set the first storage spot on a
contract a to the new value. This is really important to know
because this means we can only append new storage variables and
new implementation contracts. And we can't reorder or change
old ones. This is called stora
ge clashing. And in the
implementations we're going to talk about, they all address
this issue. The next one is called function select or
clashes. When we tell our proxies to delegate call to one
of these implementations, it uses what's called a function
selector to find a function. A function selector is a four byte
hash of the function name and the function signature. Don't
worry about the function signature for now. Now, it's
possible that a function in the implementation contract has the
sam
e function selector as an admin function in the proxy
contract, which may cause you to do accidentally a whole bunch of
weird stuff. For example, in this sample code in front of you
even though All these functions are totally different, they
actually have the same function selector. So yes, we can run
into an issue where some harmless function like get price
has the same function selector as upgrade proxy or destroy
proxy or something like that. This leads to our first of the
three implementatio
ns of the proxy contracts. This is called
the transparent proxy pattern. And it's actually going to be
the pattern that we're gonna be demoing to you today. In this
methodology, admins are only allowed to call admin functions.
And they can't call any functions in the implementation
contract. And users can only call functions in the
implementation contract and not any admin contracts. This way,
you can't ever accidentally have one of the two swapping, and
having a function selector clash and you
running into a big issue
where you call a function, you probably shouldn't have, if
you're an admin, you're calling admin functions. If your user
you're calling implementation functions. So if you're an
admin, and you build some crazy, awesome defi protocol, you
better come up with a new wallet address because you can't
participate. The second type of proxy we're going to talk about
is the universal upgradeable proxy, or the op amps. This version of
upgradable contracts actually puts all the log
ic of upgrading
in the implementation itself this way, the solidity compiler
will actually kick out and say, Hey, we got two functions in
here that have the same function selector. This is also
advantageous because we have one less read that we have to do, we
no longer have to check in the proxy contract if someone is an
admin or not. This saves on gas, of course, and the proxy is also
a little bit smaller. Because of this, the issue is that if you
deploy an implementation contract, without any
upgradeable functionality, you're stuck. And it's back to
the Eat method with you. And the last pattern or methodology that
we're going to talk about is the diamond pattern, which does a
number of things. But one of the biggest things that it does, it
actually allows for multiple implementation contracts. This
addresses a couple of different issues. For example, if your
contract is so big, and it doesn't fit into the one
contract maximum size, you can just have multiple contracts
through this mu
lti implementation method. It also
allows you to make more granular upgrades, like you don't have to
always deploy and upgrade your entire smart contract, you can
just upgrade little pieces of it. If you've chunked them out.
All the proxies mentioned here have some type of Aetherium
improvement proposal, and most of them are in the draft phase.
And at the end of this explainer, we will do a demo of
showing you how the delegate call function works. And the end
of the demo is right now. So let's l
ook at delegate call. Now, we're going to learn about
how to actually build these proxies how to build these
upgradable smart contracts. And to do this, we first need to
learn about this delegate call function. And it's going to be
really similar to the call function, which we learned much
earlier. If you haven't seen that, be sure to go back to our
Harnett. And if T's we have a sub lesson in there, about EVM,
opcodes, and coding and calling and we'll give you all the
context for delegate call.
Like I said, in the explainer, it's
very similar to call however, the way that I think about it is
one contract says Oh, I really like your function, I'm going to
borrow it myself. And we're going to be looking at solidity
by example. I'll leave a description in the GitHub. And
all the code for this will be in the GitHub associated with this
lesson as well. Now we have two contracts, we have this contract
B that we're going to be deploying on remix and it looks
like a real minimalistic real simp
le contract. We have a
couple of storage variables here. And then we have a
function that updates our values, we have a function
called Set VARs and updates are you into public num? Now as we
learned before, whenever we have some type of contract with
storage variables they get stored in, in this storage data
structure that's index starting from zero, right now are you
into public num is that index zero, or sender's at index one,
our values and index two etc. Now we're going to deploy a
contract
a and now this contract is actually going to use the
delegates call function. Now a contract a this is going to look
a little bit different, but it's still going to have this set
bars functions, except it's going to make a delegate call
function call to our contract B. Now in our lesson 14 with NF TS
we learned about call API dot encode with signature API dot
encode etc. So if you're unfamiliar with function
selectors, if you're unfamiliar with if you're unfamiliar with
this syntax, be sure to
go back to lesson 14 To understand ABI
dot encode with signature and contract dot call. The
difference here is we're doing contract dot delegate call. What
this call does is something very similar to call. Normally, if we
did contract dot call on this contract, we would just call
this, we would just be calling this function set VARs, which
would update contracts B's storage, but instead we're
saying Hey, call that set VARs function, and then pass this as
an input parameter, but call it in our co
ntract, call it on
contract a, we're kind of borrowing a function for our
contract. And so instead, what we're going to do, is we're
going to borrow this set bars and run the set bars function
over here. Now the difference is instead of num equals num The
variables could be named different than the variables on
contract a. So instead of num equals num, our contract is
going to say, hey, whatever that storage of zero have that equal
to whatever we pass as an input parameter. And if that's a
littl
e bit confusing, just stay with me. Let's go ahead and
let's see this in remix. So I'm going to copy paste this code
into remix here. So we can kind of test and see what this looks
like. Again, there's a link to this in the GitHub repo
associated with this course, feel free to pause the video to
grab this link. It's solidity, hyphen, by hyphen,
example.org/delegate call, or you can just grab the code
directly from lesson 16 Hardhead upgrades. So let's compile this
code. And let me show you what
I mean. So I'm going to compile
it, and we'll go to the Run tab. And first let's deploy this
contract beam, we'll hit Deploy, we now have a contract, num,
center and value are all blank, we'll update the number to
something like 777, we'll hit Set VARS. Set VARs will change
the storage variable num to 777. And then we're changing the
sender and the value, sender, and value is zero. Now let's
deploy contract a. So we'll scroll back up contract a
deploy, of course, we're on the JavaScript VM. Now
we have this
contract a with num value in Cinder are also all blank. But
when we call set VARs, it's going to borrow this set VARs
function from contract B and run it in contract a, you can almost
think of it as if we're copying, set VARs and pasting it into our
contract a just for one run, and then immediately deleting ADM,
that's what this delegate call function does. So when I call
set VARs, we're going to pass it this contract address as an
input parameter. So it knows to call this contract
said virus
function. When I pass it the address, and I pass 987. Since
we're borrowing the function, we're not going to update this
num. On contract B, we're going to update the num on contract a.
So when I hit Set VARs, we see num now has 19. Seven, we see
Senator and we see value still being zero here, because again,
we're borrowing this function and running it here. Now the way
that this works, is it actually doesn't look at the names of our
stored variables, it looks at the storage slots. So
when we
borrow this function using delegate call, so we could have
this these variables we named anything instead of num, we
could call this first value. Senator, we could call something
else. And then value we could call foo, or whatever you want
here. And when we borrow this function using delegate call,
instead of grabbing the actual names of the variables, our
contract will swap out these variable names with the storage
slot. So it says oh, okay, well, in contract B, you're accessing
the nu
m variable, which is, which is at storage slot zero.
So when we borrow set bars and contract a with Delegate call,
we'll say storage slot zero is going to equal that
underscore num. Which are this contract storage slot zero is
first value. So we'll say first value equals underscore Now,
something else is going to be stored slot two, so it's gonna
say okay, storage slot two, we're gonna update storage slot
two to message that sender. Okay? value here is storage slot
three. So whatever's in storag
e, slot three, will update with
message dot value like this. So that's essentially what's going
on behind the scenes. So let's go ahead and let's delete those
and redeploy. redeploy them. So we'll deploy contract be
deployed contract a, right now in B, once again, if we do 1234,
set VARs, we have 123. And then contract a. Now even though
these variables have different names, we could grab contract
B's address, paste it in, do 654, hit Set VARS. And first
value is now six by four. So delegate cal
l allows us to
borrow functions, and then just transposes whatever is in here
to the storage location equivalents. And the other thing
that's interesting is even if you don't have variables, it'll
still save to storage slots. So in contract a, if we didn't have
any of those variable names, storage slot, 01, and two would
still get updated. Now here's where things can get really
interesting. Let's delete our contract again. And let's change
the type of our contract A's first value to from a UNT t
o a
Boolean. Let's save that. And now let's deploy contract a. Now
when we call set VARs, on our contract a, it's still going to
use the Set VARs function of contract B, which takes a
Yewande and assigns the first storage slot that number we pass
it, but our first storage slot is now a Boolean was so what do
you think's going to happen now? Well, let's try it out. Let's
copy contract B's address, paste it in here. We'll pass we'll do
tu tu, tu as our input parameter, we'll hit Set VARS.
Our tran
saction actually does go through. And now when we look at
first value, it says true. Hmm, that's really weird. What if we
change set VARs to zero, and hit Set VARS. And now, first value
is false. In storage here, when we add a number with set VARs,
it's going through because it's just setting the storage slot of
the boolean to a number. And when solidity reads it, it goes,
Oh, well, first value is a Boolean. So if it's anything
other than zero, it's going to be true. So this is how you can
actua
lly get some really weird results. If your typings are
different, or if your stored variants are different. What if
we made this an address. So this is where working with Delegate
call can get really weird and really tricky, really fast. All right. Now, with all this
being said, let's turn up the heat. And let me show you a
small proxy, a minimal proxy example, that shows how a
contract can be used as a singular address, but the
underlying code can actually change. And all the code we're
gonna b
e working with, once again, in the hardhat upgrades,
FCC sub lesson, small proxy dot Sol, and you can go ahead and
copy paste this code if you want to follow along. So you don't
have to code along with me here. But you absolutely can if we
want. Now, I will say this is going to be one of the most, if
not the most advanced section of the entire course. So feel free
to go ahead and skip over this sub lesson. If you want to just
move on to learning how to actually build these proxies,
without reall
y understanding what's going on behind the
scenes. However, it is still really powerful if you do
understand what's going on behind the scenes. So I have
this minimalistic starting position right here. I have
small proxy is proxy. And I'm importing this proxy dot sole
thing from up in Zeplin. openzeppelin has this
minimalistic proxy contract that we can use to actually start
working with this delegate call. Now this contract uses a lot of
assembly or what's something called you'll. And it's an
i
ntermediate language that can be compiled to bytecode for
different backends. It's a sort of inline assembly inside
solidity and allows you to write really, really low level code
close to the opcodes. Now we're not going to go over you'll but
I'll leave some links to the you'll documentation if you want
to learn more. Even if you're a really advanced user, you really
want to try to use as little EULA as possible. Because since
it is so much lower level, it is much easier to screw things up.
Howe
ver, like I said, for this example, we are going to be
using a little bit of you'll now in this proxy that we're going
to be doing, we have this delegate function, which inside
this inline assembly, which is you'll, it does a whole lot of
really low level stuff. But the main thing that it does is it
goes ahead and it does this delegate call functionality. If
we look here, we can see it's using a fallback function and a
receive function. So whenever it receives a function that doesn't
recognize i
t'll call fallback, and fallback calls our delegate
function. So anytime a proxy contract receives data for a
function it doesn't recognize it sends it over to some
implementation to some implementation contract where it
will call it with Delegate call. In our minimalistic example
here, we have a function called Set implementation, which will
change where those delegate calls are going to be sending.
This can be equivalent to like upgrading your smart contract.
And then we have implementation he
re to read where that
implementation contract is. Now to work with proxies, we really
don't want to have anything in storage, because if we do
delegate call, and that delegate call changes, some storage,
we're going to screw up our contract storage. The one caveat
though to this, we do still need to store that implementations
address somewhere, so we can call it so ERP 1976. It's called
the standard proxy storage slot, which is an Aetherium
improvement proposal for having certain storage slots
s
pecifically used for proxies. And in our minimalistic example
here, we set bytes 32, private constant implementation slot to
that location in storage. And we'll say, okay, whatever is
that this storage slot is going to be the location of the
implementation address. So the way our proxy is going to work
is any contract that calls this proxy contract. If it's not this
set implementation function, it's going to pass it over to
whatever is inside the implementation slot address.
That's what we're go
ing to build here. So we have the small proxy
is proxy. And we'll create a real minimalistic contract. So
we'll say contract employee, mentation A, and we'll just give
it a U and 256. public value and then function set value, you add
256 new value, public will say value equals new value So this
is going to be our implementation. So now anytime
somebody calls small proxy, or small proxy contract, it's going
to delegate, call it over to our implementation, a, and then save
the storage in our small
proxy address. So we're going to call
our small proxy with the data to use this set value function
selector. So let's make it a little easier just to figure out
how to get that data by creating a new helper function, do
function, get data to transact. And we can get the data using
the API dot encode with signature that we learned in an
earlier lesson. So function get data to transact, we'll pass it
a un 256 number to update. So we'll give this the number we
want to call a new value. We'll have
this be public pure, that's
going to return a bytes. Memory. And we'll just say
return ABI dot ENCODE. With signature set value, you went to
56, comma, number to update. So you'll remember this from our
call anything section. And if you don't remember how to do
that, remember to refer back to our NFT section to learn how to
call anything and use ABI dot encode ABI dot encode with a
signature and call anything with its raw bytes bring and get the
data to transact. And we know that when we call im
plementation
a, from our small proxy, we're going to update our small
proxies storage. So we'll create a little function in solidity
just to read our storage in small proxy. We're gonna say
function, read storage. And this will just be a public view, will
do returns, returns you into 256, value at storage slot,
zero. And we are going to use a little bit of assembly here
since we are doing all this low level stuff. And we're going to
call the s load up code to read the value at storage slot zero,
we'll say value at storage slot zero, and we're going to set it
and then in assembly, this is how we set things, we're going
to set it equal to s load of storage slot zero, and then it
will return this value here. So we're reading directly from
storage. Oops, and then we need a little parenthese here. Sorry.
So now, let's go ahead and deploy our small proxy. And
let's deploy our implementation A. Now our small proxy has a
function called Set implementation. So we're saying,
okay, anytime we cal
l this proxy contract, we're going to
delegate call the functions over to here. So we're going to grab
current implementations A's address, paste it into set
implementation 77. So this is the data of set value you in
256, with that number to update encoded in it. So if we call our
small proxy, with this data, our proxy contract is gonna go, oh,
okay, this is a function. I don't I don't see that function
here. We're going to call our fallback, right, which again, is
coming from open Zeplin. And o
ur Fallback is going to do this
delegate, which is this low level stuff, but it's basically
just doing a delegate call, we're gonna call our fallback
function, and then we're gonna get the function in the
implementation A, we're gonna borrow this function, and we're
gonna use it on our on ourselves. So if I copy this,
the implementation has been set to being this address down here.
So all the logic is going to be done here. So when I go ahead,
and I grabbed this, and paste it into call data, and
I hit
transact looks like it went successfully went through. If I
read storage, now, we see that it is indeed 777, which is
incredibly exciting. Now, this is incredibly beneficial,
because now let's say we want to go and update our code, right?
We don't like contract implementation anymore. So let's
go ahead copy contract implementation A. And we'll make
a new one called implementation B. Now, let's say whenever
somebody calls set value, we do value equals new value, plus
one, or plus two. Let'
s go ahead, let's save this. Let's
compile this. And let's deploy implementation B, we'll grab
implementation B's contract address, we'll call it onset
implementation in our proxy. And essentially, we have now
upgraded from implementation a to implementation B. Now, if we
use this same data here, we're still going to call set value
was 777. But instead, we're now delegate calling to
implementation B, instead of implementation A. So if I call
if I put this data into the low level call data, and I
hit
transact, it looks like it went through. Now I read storage and
now is 779 since doing value equals new value plus two so
this is a minimalistic example of how upgrading actually works.
Now, this is incredibly beneficial, because we can
always just tell people, Hey, make all your function calls to
small proxy, and you'll be good to go. But like I said before,
this also means that the developers of this protocol can
essentially change the underlying logic at any time.
This is why it is so im
portant to be sure to read contracts and
check to see who has the developer keys. And if a
contract can be updated, if a contract can be updated, and a
single person can update it, well guess what, you have a
single centralized point of failure. And technically the
contract isn't even decentralized. Now, something
else I was talking about in the video is function clashes
function selector clashes. Right now, whenever we call set
implementation, the proxy function sedimentation gets
called becaus
e we don't trigger the fallback because we can see
the function is here. However, if I have a function called Set
implementation, in our implementation, this one can
never be called, whenever we send a function signature of set
implementation, it's always going to call the one on the
proxy. This is where the transparent proxy that we're
going to be working with can help us out here and the
universal upgradable proxy can help us to, and I'm not going to
go too much deeper into these now. But we'v
e left some links
in the GitHub repository to teach you more about these
selector clashes. And how those two proxy patterns that I just
mentioned, the transparent and universal upgradable can get
around these, if you're confused by anything in here, go into
this discussion thread and make a new discussion about proxies
make a new discussion about the Assembly about the you'll set
implementation, this is a great time to connect with other
people taking the course and ask questions here, because I
know
that this is a really advanced section, and requires you
haven't gone through a lot of those sub lessons that we've
gone before. And if it takes you a couple times of playing around
with solidity and playing around with remix, I definitely
recommend you do so this is the section we're seeing really is
believing. And I want you to jump into remix. And I want you
to test this. And I want you to play around with this and see
what you can break and fiddle with. But with all that being
said, we
finally have all the knowledge that we need to build
our hardhat project that deals with upgrades. So let's go ahead
and jump into it. Your new terminal, we're going
to do MK dir, hard hat, upgrades, FCC, CD, hard hat,
upgrades, FCC, and then code period, or file, open folder,
this folder here, now I'm going to grab that same yarn add we've
been grabbing from less than nine, we're gonna paste it in,
we're going to add all of these different parameters. And once
again, we're going to copy over o
ur hard hat config from
previous sections, we're going to copy over prettier. And we're
just going to get our basic default setup. At this point,
you might have a setup that works best for you and that you
like better. And feel free to grab that as well. So I'm going
to paste the prettier is in here. And I'm going to paste the
hard hat.config.js that we've been using, instead of running
yarn hard hat. And now we should be good to go. So let's create a
new folder called contracts. And in here, we
're going to create a
new file called Box dot soul. And then it's this contract,
that's going to be our implementation or our logic
contract. So it will say pragma, solidity carrot zero point 8.7
contract box will say you into 256 internal value will do event
value changed you into 256 new value, do function store, you
went to 36 value, this will be a public function will say value
equals new value. So this store function is going to update our
variable at storage slot zero, internal, and then w
e'll just
omit value changed that new value. And then we'll just
create function retrieve, which will be a public view returns a
UNT 256 We'll just do return value. And then we're going to
create a new function called version. And this will be a
public door. And that returns a un 256. And we're just going to
have this return one. So our box contract here is going to be
version one. Now we're going to copy all this code, paste it
into a box v two dot soul and rename it to contract box v two,
we'r
e going to update the version to version two here. And
we're going to create a new function called increment. And
this will be a public function. And we'll say value equals value
plus one. And then we will emit a value changed event with
value. We're going to have one contract address originally use
the logic in box and then we're going to upgrade it to the logic
in box V two. And we're going to learn how to use all the tools
that we've been working with here to add this logic and
create this lo
gic And let's just make sure this works, we'll do
yarn, or that compile. So let's make a readme.md, we'll
say, one, we're going to upgrade box to box V two. So we're going
to make a proxy contract, that's gonna point to box. And then
later on, we're going to update it to point to box V two. Right,
so we're going to start it up pointing to box, then we're
going to have it point to box V two. And that's how we're going
to upgrade it, one of the first things we're going to need to do
is deploy a pr
oxy, we can deploy a proxy manually ourselves, and
we can build the proxy contracts and do all that stuff. That's
our first option. Hardhead deploy also comes built in with
deploying and upgrading proxies itself, where we can just
specify that we want to use a proxy. And we could specify the
type of proxy that we want to use. So number two is just
saying using hard hat deploys, built in proxies. And then
number three is open Zeppelin actually has an upgrades plugin,
which allows you to write som
e really simple scripts that
allows you to have a really simple API like upgrades dot
deploy proxy, and then upgrades that upgrade proxy. Now for this
section, we're going to be doing the hard hat deploys built in
proxies. However, in the GitHub repo associated with this, if
you go to scripts, there's other upgrade examples that will show
you how to use the opens up and upgrades plugin. And we're not
going to do deploy a proxy manually manually, because we
essentially just showed you how to do t
hat in our sub lesson. So
we're going to show you this, we're going to show you the
Hardhead deploys built in proxies, and if you want to use
the openzeppelin upgrades plugin, that is available in the
GitHub repo as well. So let's go ahead and do this. So we're
going to go ahead and we're going to make a new folder, or
deploy folder. And first, we're going to make a new file called
a one, deploy box dot j s. And this is going to look really
similar to everything we've been doing so far. So modul
e dot
exports, equals async function, we're going to get named
accounts and deployments. The arrow function will say const
deploy COMM A log equals deployments const deployer
equals await, get named. Accounts, do a little logging,
like this, and then we'll say const, box equals await, loi,
box, comma, we'll say from Deployer, comma, args like this,
we'll say weight con formations is going to be network dot
config dot block confirmations for Masons. And yep, we got to
grab const from hard hat. An
d then we can add the parameter in
here for proxies. And this is where we can add a ton of
information. So like I said, we're going to use the
transparent upgradable proxy from open Zeppelin, which if we
want to use it, we're going to have to add open Zeppelin. So
we'll do yarn, add dash dash dev at open Zeppelin slash
contracts. And we can tell our hard hat to deploy this box
contract behind a proxy will say the proxy contract is going to
be the open Zeppelin transparent proxy. And we're also g
oing to
do a via admin contract. So instead of having an admin
address, for the proxy contract, we're going to have the proxy
contract owned by an admin contract. And doing it this way
is considered a best practice for a number of reasons. But
we'll name this admin contract, box proxy admin and the artifact
for this box proxy admin. So we'll need to create a box proxy
admin contract to be the admin of our box. So in our contracts
folder, create a new folder called proxy. And in here, a new
file
called Box proxy admin that saw and this is going to be the
admin contract we're going to have for controlling the proxy
of our box so inbox proxy admin dot Saul will do SP DX license.
I then to fire MIT pragma solidity, carrot zero, point
8.7, say contract, box proxy admin,
like this, right? And once again, we're going to use one of
the open Zetland tools in the transparent folder. They have a
proxy admin dot Saul, which is going to be essentially what our
box proxy admin is going to be. It has
some functionality, it
has some functions in here like change, proxy admin, upgrade,
upgrade and call for dealing with upgrades. All contracts. So
we're going to import it will do import at open Zeppelin slash
contracts slash proxy slash transparent, slash proxy admin,
dot soul. And we'll say our Brock's box proxy admin is proxy
admin, like this. And to have this box proxy admin work with
the heart at deploy plugin, our constructor needs to take an
address owner as an input parameter, but we're
just going
to leave that blank. And then, and then we need to do the proxy
admin, which is just going to be blank as well. And that's it.
That's all we're going to do box proxy admin contract, which just
has all of the functionalities to do upgrades and change proxy
admin and all this stuff. We're going to deploy our box contract
behind a proxy and opens up and transparent proxy that is owned
by our Brock's proxy admin contract in the template
Aetherium contracts. GitHub repo is actually an exa
mples slash
opens up on proxies branch that will show you how to work with
different types of proxies. And there will be a link to this in
the GitHub repo associated with this course. And then we'll do
our verification, say, chains dot includes. And we'll go
ahead, I'm going to copy paste our hard hat helper config from
our last project, which has development chains, hard hat and
local host that we export, will import this as well. const
development chains equals require dot slash helper hard
ha
t config. If development chains dot includes network dot
name, and process dot env dot ether scan API key log,
verifying that the same as we've done a way to verify last, we're
going to copy over our utils folder. So just copy, paste, we
now have verify, which will import it in here. So we'll do
const verify equals require dot dot slash utils, slash verify,
await verify box dot address. And then args is going to be
blank. So we'll just do blank here, and boom. And that's going
to be it for our b
ox deployment. So we can test this out by
running yarn Hardhead deploy, see if everything works here.
And that's it done. And then we should do actually, we should
add log beach true as well. Let's run this again, much
better. So you can see we actually deploy a couple of
contracts. So we deploy our box proxy admin, which is going to
be our admin contract, then we deploy box implementation. So
hard hat deploy, went ahead and took our box contract and
renamed it to box implementation and then dep
loyed it, then it
deployed our box proxy here. So anytime we call this address, it
actually will point to the logic of our box. Now what we can do
is we can write a deploy script to deploy box, the box v2
implementation, and then upgrade our box to box V to create a new
deploy script called Zero to deploy box v2 dot j s or deploy
box two dot j s. And we'll do something really similar here.
Right, so I'm just going to copy pretty much of this, paste it in
here. Now we're going to do const box v t
wo equals await,
deploy. Box v two, comma, from Deployer. Log True RMS blank,
weights confirmations, it's going to be network dot config
dot lock. Or masons, then I'm going to
copy the verification code, copy this, paste it here, server,
this is going to be box v2 dot address. Okay, great. So we now
have some code where we can deploy box and box v2. Now let's
go ahead and write a script to actually upgrade these. So we'll
do a new folder, scripts. New File, and we'll call it upgrade
box dot j s.
Now we're going to do it the manual way here. And
the reason we're going to do it the manual way is because I want
to show you exactly the functions that we're calling to
do this upgrade process. However, hard hat deploy also
comes with an API to make it really easy to actually just
upgrade your box contracts. This is gonna be script. So we'll
start off with async function main. And then we'll copy paste
our traditional script, main dot then process exit blah, blah,
blah, that whole thing. We'r
e first gonna get the box proxy
admin contract. We'll say box const. Box, proxy admin equals
await ethers dot get contract, box proxy admin and we got to
import you Here's from Hardhead, then we're going to get the
actual proxy, which is our transparent proxy. So we'll say
const transparent proxy equals await ethers dot get contract,
box underscore proxy. Since hard hat deploy, we'll just name the
proxy the name of the implementation underscore proxy.
And then of course, we're going to need our
box V to contract.
So say const. Box v two equals await ethers dot get contract,
box V two. Now we can say const, upgrade TX equals a weight box
proxy admin, that upgrade. And we're going to call the upgrade
function on our box proxy admin, which calls it on our
transparent proxy, which will change the implementation from
box one to box two. So box proxy admin, that upgrade, we're going
to upgrade the transparent proxy dot address to our box B to that
address, it will look in our box proxy, a bo
x proxy has an
upgrade function which calls upgrade to on our transparent
upgradeable proxy will do a weight upgrade TX dot wait one.
And now to work with the functions on our box v two,
we're gonna say const proxy box equals await ethers dot get
contract. At box v two, we're going to get the box v two ABI
however, we're going to load it at the transparent proxy
address. This way ethers knows okay, we're going to call all of
our functions on the transparent proxy address. But this proxy
box is g
oing to have the ABI of box v2, which is what we want.
Now we can say const version equals await proxy box dot
version. And we'll say console dot log version. And if we want
to compare this to its original implementation, before we
upgrade, we can do the same thing. We can say const proxy
box v1, let's actually call this proxy box V to say cost proxy
box v one is going to be this exact same thing equals this call box here. And
we'll do const version equals weight, right proxy box v1 dot
version,
console dot log version, or version v1, version
v1. We'll call this version v two, version v two, and proxy
box V two. So we'll get our version v one will upgrade. And
then we'll see at that same address what the version
function now returns. So let's open our terminal. If I run
yarn, our head node will spin up a node will have deployed our
admin our implementation, our proxy, and then our box V to
implementation. I'll make a new terminal. And I'll run yarn,
hard hat run scripts upgrade box tha
t J S dash dash network,
local host. And we should see that box actually update. And
that's exactly what we see. And then let's do well, it's I
forgot to do.to string in here. But it goes from version one to
version two on that exact same address. And with that little
code, we have successfully learned how to upgrade our smart
contracts programmatically. Now, like I said, in the GitHub repo
associated with this lesson, you can also check out the upgrades
plugin from open Zeppelin. So to work wit
h that, you would just
do upgrades dot deploy proxy proxy to call the Prepare
upgrade function, and then upgrade upgrade proxy just like
that. They also have an upgrade tutorial, step by step for
hardhat that you can follow along with as well. Now I know
this was an advanced section. And I know we went a little bit
quick here. But honestly, if you just finish this section, not
only have you completed all these other sections that make
you a really powerful smart contract developer. But you've
le
arned some really advanced stuff here. We've gone into low
level code like delegate call, we've gotten to assembly we've
gotten to you'll we've gotten to these proxy patterns, which can
really make you a an incredible stand out developer in the smart
contract space. So if you just finished this section, you
should be so so proud of yourself because we went really
fast and because there's a lot of advanced information here.
Now, like I said, 100 percent, be sure to go into the discussions tab
and
ask questions and connect and talk to other people in the
area. Maybe look at the already running discussions and jump in
and start asking other students and start asking other people
about what they've learned and how their proxies are going. And
if they made anything really cool. If you're just excited and
you want to go to the show and tell section, make a show and
tell me like hey, here's my GitHub repo for doing this
upgrade section. going here and be excited with that being said
go and ta
ke that coffee break go take that walk go to the gym go
get excited Go tell your friends we are almost done we have two
lessons left and then you're home free All right, welcome back to
lesson 17 For hardhat. Douse, you're almost done. Now for this
section, I've actually already made a video on how to code a
dowel with TypeScript and solidity in JavaScript. So we're
actually just going to play this video for this section. The
reason we're going to display this one is because I did a lot
of work
to make this one look really good. And it's still
incredibly up to date. So this is going to be in TypeScript and
solidity. However, we're going to have the JavaScript edition
of the code base in this code from the video section. If you
want the most up to date version of this delta template code, you
can use, you can select this up to date code, which goes to this
doubt template repo. I'll be updating this repo periodically
with new Dao examples and new ways to create doubts or
decentralized au
tonomous organizations. Now, before we
learn how to code a doubt, we should learn what the DAO is.
And again, I've already made a video that I've put a lot of
work into. So we're going to watch what a Dow is from a high
level first, then we're going to learn how to code a Dow. And
then our last section is going to be security and auditing. And
we're going to finish this out. So Bucklin, let's learn what a
Dow is. And then let's go ahead and build a Dow, let's do it.
Now, daos for decentralized a
utonomous organizations is a
bit of an overloaded term. But it typically describes any group
that is governed by a transparent set of rules found
on a blockchain or smart contract. And I say overloaded
because some people say bitcoin is down because the miners can
choose whether or not to upgrade their software. Other people
think that Dows must use transparent smart contracts,
which have the rules ingrained right into them. And then other
people think Tao is just a buzzword, so they just slap t
he
name relay on to any organization so that they can
get some clout. And this makes for sad, Patrick. And it's not
to be confused with the Dow, which was an implementation of a
Dow back in 2016, which set the record for the largest hack at
that time. So there's a lot of different ways to think about
and the Dow term is used in a lot of different ways. But in
essence, imagine if all of the users of Google were given
voting power into what Google should do next. And the rules of
the voting was im
mutable, transparent, and decentralized.
This solves an age old problem of trust, centrality and
transparency, and giving the power to the users of different
protocols and applications instead of everything happening
behind closed doors. And this voting piece is a cornerstone of
how these operate this Decentralized Governance, if you
will, and it can be summarized by company or organization
operated exclusively through code. And to really understand
all this, we're going to look under the hood o
f the protocol
that's setting the precedent for all other doubts and compound,
then once we look at compound, we'll understand what goes into
building one of these and all the trade offs, all the
different architectural choices mean for your group. And then in
my next video, I'm gonna have a full coat along tutorial for
developers looking to build one of these themselves. But be
absolutely sure to watch the rest of this video because it's
going to give you all the architectural fundamentals. So
you can make intelligent decisions when you get to that
section. And be sure that you and your Dow friends smash the
like and subscribe button. So we can keep giving you the best
engineer first content on the planet when it comes to smart contracts.
Let's get into it. So here we have the compound protocol. It's
a borrowing and lending application that allows users to
borrow and lend their assets. And everything about this
application is built in smart contracts. Now, oftentimes,
they're gonna wa
nt to do a lot of new things, maybe they want
to add a new token to allow borrowing and lending, maybe
they're gonna want to change some of the API parameters,
maybe they're gonna want to block certain coins, there's a
lot of different things that they might want to do. So that's
where we're gonna go ahead to governance, this is where you
can find a user interface for list of all the proposals and
all the different ballots that came to be. So here's a list of
some of the governance proposals tha
t this protocol has actually
been making to improve. And let's look at one of these
proposals that's currently actually in process. So if we
click on the proposal, we actually see everything about
this proposal, who voted for who voted against and the proposal
history here. Now, the first thing to one of these proposals
is somebody has to actually create the proposal in a
proposed transaction. And we actually see that proposed
transaction right here. If we click on this, and we scroll
down, we c
an actually see the exact parameters they used to
make this proposal. Let's go ahead and decode the input data.
And we can see this is exactly what this proposal looks like.
The way that they're typically divided is they have a list of
addresses and the list of functions to call on those
addresses. And then obviously, the parameters to pass those
addresses. So this proposal is saying, Hey, I would like to
call Support Market on this address, set reserve factor on
this address. Here are the param
eters we're going to pass.
They're obviously encoded with bytes. And then here's the
description string of what this is doing and why we're actually
doing this. The reason we have to do this proposal governance
process is that these contracts likely have access controls
where only the owner of these contracts can actually call
these Do functions, and the owner of these two contracts is
likely going to be this governance style. And values.
Zero just means that we're not going to send any eath alo
ng
with these transactions, once a proposal has been created, after
a short delay to becomes active, and this is when people can
actually start voting on them, this delay between a proposal
and an act of vote can be changed or modified, depending
on your doubt, then people have some time to start voting on
them. And if it passes, which this one overwhelmingly did, it
reaches succeeded, we click on this transaction again, and we
go to the compound governance contract. And we scroll down to
contra
ct, right as proxy, we can actually see the exact function
that the people call to vote, namely cast by vote, cast, vote
by signature and cast vote with reason. We'll talk a little bit
about the exact differences between these in our next video.
But these are the functions that they're actually calling. And if
you go to the compound app, and we go over to vote, this is a
user interface you can actually vote through to make it easier
if you're not as tech savvy. So you can vote right through this
app dot compound at finance. Or you can just send the
transaction yourself. Once all those votes happen. It reaches
this queued stage. Now what is queued mean? Well, before a
proposal actually becomes active, there's a minimum delay
between a proposal passing and a proposal being executed. So
somebody has to call this cute function. And it only can be
called if a vote passes. And it says, OK, that postal ID has
been cued, and we're going to execute it soon. Now, if we go
to a different proposal
, like this one, for example, we can
see it has been executed, we can see somebody called this
executed function. And they executed Proposal A detail. So
this is going to be a full example of the lifecycle of a
proposal going through this process. Now there are a couple
that even failed, a whole bunch of people voted against this.
And if you scroll down, you can see it was created, it was
active, and the majority of people voted against. So that's
where it stops. Now oftentimes, just putting one
of these
proposals through isn't enough to really garner some votes for
it, you generally want a forum or some type of discussion place
to talk about these proposals and why you liked them or don't
like them. Oftentimes, a discourse is one of the main
places that people are going to argue for why something is good,
or why something is bad. So people can vote on these
changes. And again, snapshot might be one of these tools that
you use to figure out if your community even wants something
before
it even goes to vote, you can join one of these, and with
your tokens actually vote on things without them being
executed just to get the sentiment or like I said before,
you could build your protocol in a way that snapshot actually
helps you with the voting process. All right, now you've
seen the protocol that has been influencing all the other
details on how to vote. Now, you know, now that we know what a
doubt looks like, let's talk about the
architecture and tools that go into building one
of these and
additionally the trade offs that they have. And the first thing
to talk about here is going to be the voting mechanism. Now
voting in Decentralized Governance is critical to these
days, because sometimes they do need to update and change to
keep up with the times. Not all protocols need to have a Dow,
but those that do need to have a doubt need a way for the
participants to engage. This is one of the most important
questions to ask him to tell your communities. How do I
participate?
How do I engage in this doubt? How do I help make
decisions and you'll find this is a bit of a tricky problem to
solve. Now, an easy approach to this problem is going to be
using an ERC 20 or an NFT token as voting power. Similar to what
we saw with compound use the comp token to vote for different
proposals seems simple enough, right? Boom, problem solved
gray. Now, this actually might be the right approach for
certain doubts. But it also runs the risk of actually being less
fair. Because when
you tokenize the voting power, you're
essentially auctioning off this voting power to whoever's got
the deepest pockets, whoever has the most money gets to pick the
changes. So if it's only the rich people who get to vote,
then it's highly likely that all the changes in the protocol are
going to benefit the rich, which doesn't really seem like that
great of an improvement over our current world. And if T's are
interesting, because they have this non fungible component, but
yet, even they still
run into this issue. Additionally, if you
buy a whole bunch of votes, you make a bad decision and then
sell all your votes. You as an individual don't really get
punished, you just punish the group as a whole. But you being
malicious, you can get away with pretty scot free now again, this
voting mechanism is going to be correct for some groups, but for
other groups, maybe not. It really just depends on what your
down community setup is going to look like. Now the next one
we're going to talk abo
ut is skin in the game. Now metallic
has actually written a lot about this and I highly recommend you
read his article, link in the description to see that the skin
in the game method means that whenever you make a decision,
your vote is recorded. And if that decision leads to a bad
outcome, your tokens are axed. You get punished for making evil
or bad decisions for your Dao and your protocol. I like this
mentality because even if you buy a ton of tokens and decide
to be ill with it, you can be
held accountable for your bad
decisions. Now, the hardest part about this, though, is gonna be
how do we decide as a community? What is bad outcome? How do we
actually punish these people? And that's easy, because the
answer is, I'm not sure. Now, the third method of this voting
mechanism is probably one of the most interesting ones, but also
the hardest ones to implement. And this is proof of personhood,
or participation. Imagine that all users of the compound
protocol were given a single vote
simply because they use the
protocol. And even if they had 1000 wallets, they use the
protocol. One human being means one vote, this would be amazing,
and a far more fair implementation where votes
couldn't actually just be bought. The issue, however,
comes in something known as civil resistance, how can we be
sure that it's one vote equals one participant and not one
participant pretending to be 1000s of different people, so
they get more votes. This method hasn't really been solved yet.
But I'
m willing to bet some very clever engineer will do some
amazing chainlink integration, because proof of personhood is
basically just off chain data that can be delivered on chain.
And that's exactly where channeling shines. Now, as you
can see all of these methods and even more that you probably
think of aren't that far fetched. And we actually see
these exact same methods happening in the real world.
Proof of personhood or proof of participation might just be the
exact same as kind of the regul
ar government voting that
we see every day. In the United States, at least one person gets
to vote for one president, you can't go around making a whole
bunch of fake people and voting for president. But in companies
the ERC 20 voting standard kind of applies, the more shares of a
company you have maybe the more voting power you have in that
company. So we can draw parallels between the real world
and how voting and governance is going to work in our smart
contracts. And in fact, you should draw
parallels and look
for inspiration from the web to space. Now when it comes to
implementation of the voting, I put them into two categories on
chain voting and off chain voting on chain voting is
exactly what we saw with compound, the smart contract on
chain, your voter, you call some function called vote with your
meta mask your ledger or whatever, send a transaction and
vote, you voted Congrats, you can wear your little sticker now
call that function and you send a transaction, you send a
tra
nsaction, what are the transactions use that are kind
of annoying and kind of costly? Oh, that's right gas, I imagine
you have 10,000 people in your community and it costs $100 To
vote per person, you're now costing your community $1
million. Anytime you want to change anything. This is
obviously insane, and not very sustainable for your community
pro here is that the architecture is really easy.
Everything's going to be transparent, everything's going
to be on chain. And that's really good. But
yes, the con is
that you're going to break the bank account for a lot of people
potentially. Now there are a lot of variations of this to help
solve some of these problems, especially the gas problem. One
of the ones that I'm incredibly excited for is this one called
governor's seat, where they use some random sampling to do some
quadratic voting to help reduce costs while increasing civil
resistance. You want to learn more about that one to be sure
to read about it in the description. So on ch
ain voting
is the simplest one here. But let's talk about off chain
voting. To cast the vote off to decentralized context, relax,
relax, you can vote off chain and still have the 100%
decentralized, you can actually sign a transaction and sign a
vote without actually sending to a blockchain and therefore
without actually spending any gas. Instead, what you can do is
send that signed transaction to a decentralized database like
IPFS, count up all the votes in IPFS. And then when time comes,
deliv
er the result of that data through something like an Oracle
like chain link to the blockchain all in a single
transaction. Alternatively, what you could do is you could replay
all the same transactions in a single transaction to save gas,
this can reduce the voting cost by up to 99%. Right now, this is
an implementation and one of the most popular ways to do this is
through snapshot. And I'm just dying for someone to make a
chain link integration because it's going to be so much safer,
more secu
re and better and blah, blah, blah, die for it. This is
your call to action go build this thing. This option. voting
mechanism obviously saves a ton of gas to the community and can
be a more efficient way to store these transactions anyways,
however, it needs to be implemented very carefully. If
you run your entire Dow through a centralized Oracle, you are
essentially reintroducing a centralized intermediary and
ruining the decent quality of your application. So don't do
that. And if you made it
to this point of the video, give
yourself a little pat on the back. You're doing fantastic.
Fantastic. Like I said, I have a video coming out after this one
it's going to show you end to end how to build one of these
from scratch let's learn about some of the tools that you can
use to help get you up to speed quicker. Now there are a number
of no code solutions that can go into building one of these
dowels Tao stack Aragon. Just kidding, this is Aragon, colony
and Tao house are all alternatives
that can actually
help you with the upside of running a Dao and building a
doubt. However, if you want more granular control and you don't
want to have to pay any the fees associated with these protocols,
you might want to Do it from scratch. Now let's talk about
some of the more Cody solutions that you can use snapshot is one
of the most popular tools out there for both getting the
sentiment of a Dao. And actually performing that execution, users
can vote through this protocol with their actua
l tokens, those
transaction gets stored in IPFS. But none of it actually gets
executed. Unless the doubt chooses to, this can be a great
way to get a feel for what your Dow wants to do. And Optionally,
you can send the transactions and execute the votes as well
highly recommend checking out Zodiac which is a suite of
database tools for you to implement into your Dallas as
well tally is another one of these UIs that allows people to
see and actually vote and interact with these smart
contracts th
rough user interface. So those of you who
don't know about Gnosis safe, you absolutely should know Safe
is a multi SIG wallet. And the reason I put this on the list,
even though it's adds kind of this centrality component is
that most dowels in the beginning, are probably going to
start with some type of centrality, it's much easier to
be fast when you don't have 1000s of people to wait for a
vote. And in the beginning, any protocol is going to be
centralized to some degree anyways, using a mult
isig, we're
voting happens through only a few key members can be good in
the beginning for your dials, and often emergencies as well.
But just keep in mind, when you add one of these, you are adding
this level of centrality, and then of course opens up and
contracts we love opens up and contracts. These are the
contracts that we're going to be basing our Dao code along.
Alright, so that's all the tools. That's the architecture.
One more thing before I let you go legality, the future of Dows
is i
nteresting for all these reasons we just talked about,
but especially on a legal front, does it make sense for a doubt
to live by the same regulation as another company? Why would
you even force it down to do something, you'd have to enforce
them to all vote a certain way if the government tells you to,
it's a little gray, it's hard to nail down who to even keep
accountable for these days and the United States at least you
can actually form your own Dow and have it legally recognized
in the stat
e of Wyoming. This is something I want to do. So we'll
just have to see what happens there. Oh, at this point, you
have been injected with all the Dow knowledge you need to
succeed and thrive with this new amazing technology and these new
amazing concepts. And I found them kill baby. All right, well,
you heard him it's time to build. Like we said, All the
code is going to be located in less than 17 Here, let's jump
in. In this video, we're going to
show you how to build your own doubt inspired b
y compound now
this is going to be 100% on chain voting and on chain
governance. We're going to show you the easiest way to spin up
an NFT or ERC 20 voting type down all using solidity and
hardhat. Now if you haven't watched my last video going over
the architecture of Dows, what goes into one of these be
absolutely sure to watch that video first and then come to
this video. Because that video explains all the philosophy
behind what we're doing here we're going to be using opens up
and contracts
and a hard hat framework to build this all in
solidity. If you want to see a brand new or Pythonic version of
doing this, check the link in the description because we did a
video over at the chainlink hackathon recently. And
additionally, additionally, we know that because we're doing
this 100% on chain gas fees are going to be expensive. So I'm
really looking forward to somebody doing a chain link plus
IPFS plus snapshot integration so that we can do all this off
chain. And once that exists, y
ou already know I'm gonna make a
tutorial on that. And if you like this style of content, be
sure to smash that like button, subscribe and leave a comment in
the comment section to let me know what you want to see. Next,
let me know how you want to supercharge your smart contract
developer experience. So let's jump in. Right, so here's what
we're going to be building, we're gonna have a very basic
smart contract here, right, it's called box and all it can do is
store a value and then retrieve a
value. But the thing is, its
ownable. And only the owner of this contract can call the store
function and guess who the owner is going to be the owner is
going to be the doubt. So only through a process of governance,
can anyone store a different function here. And once we're
done, we're going to go through the entire process of proposing
voting queueing and then executing a transaction in a Dao
to update our Bach contract. And that's one of the beautiful
things about these these Dao setups is t
hat they're
completely modular, right. And so when I go through the whole
process, I'll do Hardhead tests here, which my tests right now
are set up to just do everything, we're going to see
every single step that this Dow is going to take. So we see box
starting value is going to be zero. And then all of this stuff
is going to the governance process. These are just some
some notes. Basically people are voting, queueing and executing.
And then at the end, we chained the value of the box contract
through a voting process. And that's exactly what we're gonna
show you how to do today. Now remember all the code for what
we're going to be doing here is in my doubt template GitHub repo
so if you ever get lost, feel free to refer back to this to
get started. And additionally, if you want to see the Pythonic
version of this, feel free to go back to the downmix The main
thing is though that all the contracts are going to be the
same no matter what brownie hard hat, dab tools, foundry, it
doesn't
matter. So the first part of this section is gonna be
exactly the same. And here's our agenda here. First, we're gonna
write the smart contract. So if you're not familiar with
Hardhead, who cares, we're gonna be doing the smart contracts
first, then we are going to write deployment scripts. And
this is where your hard hat knowledge is going to come into
play. We're going to be writing our deployment scripts in
TypeScript here, because TypeScript is phenomenal. If
you're unfamiliar with TypeScri
pt, I challenge you to
rewrite this in JavaScript and make a JavaScript version. And
then finally, we're going to write some scripts to interact
with our governance with our deployed contracts. Now, a quick
note, this isn't how I originally built this, I didn't
just read smart contracts, write deployment scripts, write the
scripts, and then the tests and boom, I was done, I had a back
and forth between tests, smart contracts, deploy scripts, etc.
If you're thinking, Oh, my goodness, that's so ea
sy for him
to go through this. So seamlessly. When I originally
wrote this code, it was a lot of back and forth. And that's how
you should be developing, you're going to be moving between tests
and smart contracts and stuff. Additionally, in this tutorial,
we are going to show you some sick Hardhead skills. So you are
not only gonna learn how to build a Dow, but you're gonna
learn some really advanced Hardhead skills. So let's jump
in. Let's do this. So the code editor I'm using is Visual
Studio
code. So make sure you have a code editor up and ready
to go. And you'll need a couple of prerequisites here. Again,
the prerequisites are in the GitHub repository, we're going
to need Git, no J, s. And yarn, if you want to just clone this
repo and follow the instructions here to get started, you
absolutely 100% can and then you don't even need to build this
from scratch. But we're going to want to learn to build this from
scratch. So let's just start git dash dash version. Great, we
have Git n
o dash dash version. Great, we have node, and then
yarn dash dash version. Great, we have yarn, we can get
started. So everything that we're going to be installing
here for packages is going to be a dev dependency. So the first
thing we need to do is do yarn, add dash dash Dev, hard hat, if you'd haven't already. And
now in our folder, we're gonna have node modules package dot
JSON readme and a lock. Of course, now that we have that we
can run yarn RDAP, we're gonna get the hard hat CLI up. And
we're going to have all this stuff in here, we're just going
to create an empty hardhat dot config dot j s, and we're going
to turn it to TypeScript. The advanced sample TypeScript
project has a bunch of stuff that I don't like. So we're just
going to create an empty hardhead.config.js. And perfect,
we've got a little hard hat.config.js. Now let's go
ahead and create a folder, or contracts folder. And this is
where we're going to add all of our contracts. So the first
thing contract that we're g
oing to need is the contracts we want
to have govern, which in our case is going to be box dot
soul. Now, I am actually just going to copy paste my box, that
soul here, because it's not particularly interesting. But
you could really code whatever you want here. So feel free to
pause the video, copy paste from my GitHub repo, create your own
governance contract that you want to play with, or do
whatever you want here. But for us, we just have a store
function, and retrieve function, and an event,
and then a private
value that we're going to be storing and retrieving. And
that's it. So of course, we want to fix this. We're importing
from openzeppelin contracts opens up is amazing. We're gonna
want to add this we'll do yarn, add dash dash Dev, at opens up
on contracts. And that should get rid of the box. That's all.
let's reopen box. And boom, looks like we did a detour to
that perfect. And for extensions, I'm using the
solidity, the one Blonko, slit extension. And that's what we
get this
wonderful linting things here. Great. So now we
want to check to see if this compiles if using remix, you can
compile with remix hard hat, you're gonna see how we compile
here. Or if you're using you know, Browning, we just want to
see if this compiles correctly. So run yarn had a hard hit
Compile. Looks like we ran into some compilation errors, because
we need to update this, let's use these 8.8 of solidity we'll
try to compile again. And perfect looks like we're
compiling successfully. Look h
ere, we do indeed have our
contract in here. Okay, perfect, easy part out of the way.
Already. That was so quick. Now let's start creating the next
part. Let's create the governance part. So what we're
going to be working with to build this governance platform
is we're gonna be building it off of the ERC. 20 standard, so
you're gonna get an ERC 20 token. And that's going to be
the token that you get to vote. So let's create a new file
called governance token. That's all governance token dot Sol.
And this is gonna be the code for the token that we use to
actually vote. Now we're going to create a normal ERC 20 token.
And then we're going to extend it to make it governance a bowl
and you'll understand what I mean in a second. So let's go
ahead and make this spdx license identifier. It's going to be MIT
pragma solidity we'll do zero point 8.8. And then we'll do
contract governance token, and then we'll say is ERC 20 I'm
just gonna go ahead and import openzeppelin Because open
Zeppelin has
a package where basically has everything we need
for an ERC 20 token. So we're gonna say import at open
Zeppelin slash contracts slash token slash ERC 20x We can go to
their github openzeppelin contracts token, your C 20 And
we'll do ERC 20 Dotto, we'll do this for now. token, use your C 20 ditzel.
We're gonna change this, but don't worry about that yet. Now
we're going to do a un 256 Public Storage Max supply, it's
the best practice, but it's fine, we'll give this a max
supply this much was go
ing to be 1-234-567-8910 12345678. So it's
gonna be 1 million, we're gonna do 1 million of these tokens.
And then we're gonna create the constructor construct or your C
20. Give it governance token as a name, and then our symbol is
going to be GT. And for those who who don't know, when you
inherit another contract in your constructor, you can use that
inherited contracts constructor as well. In fact, I think you
have to So governance token G team, and then we'll even call
one of these ERC 20 fun
ctions called mint. And we'll mint to
message dot sender, so whoever deploys this ERC 20 contract
will just meant them everything, the whole Max supply. Now
normally, if this was a normal ERC 20 token, you'd be all done.
But this isn't a normal ERC 20 token. See, when we do votes, we
need to make sure that it's fair, imagine this for a second,
someone knows a hot proposal is coming up, they want to vote on.
So they just buy a ton of tokens. And then they dump it
after the votes over. We want to
avoid this. We want to avoid
people just buying and selling tokens to get in on governance.
So what we do is we actually create a snapshot of how many
tokens people have at a certain block snapshot of tokens people
have at a certain block. And we want to make sure once a
proposal goes through, we actually pick a snapshot from
the past that we want to use this kind of incentivizes people
to not just jump in when it's a proposal and jump out because
once a proposal hits, it uses a block snapshot f
rom the past. So
we're actually going to need to change this a little bit. We're
gonna change this from ERC 20 to an ERC 20 votes, and we can
actually see this in open Zeppelin and the extensions
slash ERC 20 votes that soul contract. If we go back to IRS
to their GitHub, we can see ERC 20 votes, they also have a
snapshot, which is pretty similar. And some of the main
functions are it has these checkpoints. So these
checkpoints are basically Hey, what is the snapshot? There's a
number of checkpo
ints, you can also delegate your tokens to
different people. So maybe you're not going to be available
to actually vote. So you say, Hey, I'm gonna give my tokens to
somebody else. You can get how many votes somebody has passed,
votes get passed, or was apply it has all these functions that
make this token, much better as a voting tool. Right makes it
much much better. So we're gonna say our contracts governance
token is ERC 20 votes and we just have to add additional
constructor, this ERC 20 pe
rmit, I'm sorry, I kind of copied
pasted that. So ERC 20 permit governance token. And right now
we have a governance token that is a little bit more capable of
doing actual voting, right, because as the snapshot has this
delegating functionality, it has these checkpoints, it's going to
be much better for doing votes in a fair way. The only thing
that we need to do though, is we need to add some overrides,
right. And we're just gonna say the functions below our
overrides required by solidity. And
this part is a little bit
boring. So I'm just going to copy paste it, feel free to copy
paste it from my GitHub. But what we're doing is anytime we
do this after token transfer, and the time we transfer a
token, we want to make sure that we call the after token transfer
of the ERC 20 votes. And the reason that we do this is
because we want to make sure that the snapshots are updated,
right, we want to make sure that we know how many people have how
many tokens at each block. Same thing with the
mint. Same thing
with burning, we want to make sure we always know how many
tokens people have at different blocks or can be at different
checkpoints I should say. And that's the most important bit at
which checkpoint Are you going to use for your token voting. So
cool. Feel free to copy that, again, from a GitHub or if you
want you can even just try the rest of the tutorial without
this and see how you fare. But cool. So now we have a
governance token and ERC 20 token that we can use for
gover
nance. So let's try to compile it. Yarn Hardhead
compile. Great, looks like things are compiling
successfully. Perfect. So our governance token looks good. Our
box looks good. Let's actually now start creating our
governance contracts. Now we're actually going to make a folder
called governance standard because this is going to be the
standard governance model. This is going to be this on chain ERC
20 and I plan on updating this in the future with no a
governance off chain or something right. So
for now,
we're calling a governance standard because this is the
quote unquote standard way to do governance, but in the air,
we're going to need two contracts actually, we're gonna
need a government work, contract that soul and then we're also
going to need a time lock that soul and this will make sense in
a second. So our gov contract dot soul, this is going to be
the contract that has all the voting code, all the voting
logic that our governance token is going to use, the Time Lock
is actual
ly going to be an additional contract that is
actually the owner. So the Time Lock and the governor contract
are sort of one in the same, but the difference is the Time Lock
is actually going to be the owner of the box contract. And
this is important because whenever we propose or cue
something to a proposal to go through, we want to wait, right?
We want to wait for a new vote to be executed. Now, why do we
want to do that, let's say some proposal goes through, that's
bad. So like, let's say we
have a box contract. And then a
proposal goes through that says, Everyone who holds the
governance token has to pay five tokens, or something like that,
right, or whatever, or who knows, right? Maybe that's
something that you don't really want to be a part of. So all of
these governance contracts give time to give time to users to
get out, if they don't like a governance update. So we always
want to have some type of timeline. So once a proposal
passes, it won't go in effect right away, it'll ha
ve to wait
some duration, and then go in effect. So that's what the
timeline is gonna be for. Governor contract is going to
have all of our actual code. Now we can cheat a little bit. Actually, we can cheat a lot of
a little bit. So opens up one has a thing called the contracts
wizard, and there'll be a link to this in the description as
well. And this opens up when wizard is a way for us to write
really basic boilerplate code right in there wizard. So right,
so if we go to the wizard contract h
ere, we can see we can
make an ERC 20 and NFT 1155. And then finally, this gov thing
here. So we can call it give it a name, we're going to call Rs.
Gov contract, during explain what all this means, and give it
a voting delay, which is the delay since a proposal is
created until voting starts. So once you create a proposal, you
gotta wait a little bit, the voting period, how long votes
should go for. And the reason that this audio is important is
because they actually do votes, voting period in
terms of
blocks. So it's an anti pattern to actually do timed based
things in smart contracts, it's much better to do block based
things. So we're saying one week, but it's that's going to
be you know, if if the average block time is 13.2 seconds,
we're gonna figure out the weak proposal threshold is going to
be the minimum number of votes and account must have to create
a proposal. So maybe you only want people who have a lot of
your governance token to make votes quorum percentage, it's
what p
ercentage of people need to vote it all. So we're saying
4% of all token holders need to vote or we could say you know,
exactly 100 tokens need to vote whatever we want to do here. We
also have some updatable settings we have Bravo
compatible Bravo is the compound type contract. So if you want to
make it integratable, with compound you can do that. Votes
comp like or ERC 20 votes, we're working with this ERC 20 votes,
we always want to do a time lock, we're gonna do the open
Zeplin implementatio
n of a time lock, you could also do a
compound implementation, we aren't going to do upgradability
here, however, I have a number of fantastic resources on how to
actually do upgradability. And if we did want to do
upgradability, it adds all this other stuff. We're not going to
do that for now because it makes it so much longer video, and
then you can add some stuff like this, but Oh, so that's pretty
much it. And I know this feels like you're cheating, or we're
just going to copy this whole thi
ng, right? Copy all that
stuff that we put in copy to clipboard, and we're going to
paste it in. Don't worry, I'm gonna explain what's going on
now. So we have our government contract. And this is governor
Governor setting Governor counting simple Governor votes,
Governor votes, quorum Franco, all this stuff. All these are
just implementations to make it easier to be governor, Governor
counting simple is a way of counting votes, Governor votes
is a way of integrating with that year's 20 contract
. Quorum
fraction is is a way to understand quorum time lock.
Obviously, it's time lock. This is going to be the base
contract. That's gonna be some settings. And we're gonna talk
about this in a minute. But let's go over what are the
functions here so we have voting delay. This is exactly the
voting delay, which we're gonna do superdad voting gelei, we're
gonna get from this governor settings contract that we're
going to set in a minute your voting period that we're going
to set in our gov sett
ings, which is this one right here.
And again, if you want to like look at all these contracts, you
absolutely can, right, if we go to contracts, governance,
extensions, we have all these in here, right? So if we look at
governance settings, we can see it has voting delay voting
period, proposal threshold, and those are right in its
constructor. And that's exactly what we're setting, right. We're
setting voting delay voting period, and then the proposal
threshold, and then we're also going to ma
ke this customizable
as well. And the rest of these that's exactly what doing
calling the quorum from the super get votes. And then again,
the super is those inherited contracts, get the state and
then we have some interesting functions we have proposed. This
is what we're actually going to do to propose new governance we
have proposal threshold, and then we have execute which
executes a cued proposal. To cancel, we have executor which
we're is going to be who can actually execute stuff, we're
a
ctually going to make it anybody and then supports
interface, you can basically ignore. But let's make this a
little bit more customizable. So we have Iboats token, this is
going to be our governance token, the timeline controller
timeline, this is going to be the timeline controller that we
make an amended. And again, we need this because we don't want
to let any proposal just go through once it passes, we want
to give people time to get out. But let's add the UN 256 voting
delay as a parameter
here. And for voting delay, we're going to
do this, we're going to set it as our governor settings, we're
going to do a UNT 256 voting period. And we're going to add
that right here. And this means 45,000 blocks is approximately
one week. And that's what that means we're going to leave
proposal threshold to zero because we don't really want to
change that we want to let anyone make a proposal. And then
we're just going to add you in 256 underscore core percentage to this. So Governor
votes quor
um percentage corporate senators. So now this
is completely customizable, for voting, delay voting period,
core percentage for whatever you want it to be. And believe it or
not, that's it. Now you have a simple governance contract.
Thank you, openzeppelin, for doing 99% of the work for us. So
that's it. So this contract, it's going to have all these
functions that we're going to go over for proposing for executing
and for queuing, different proposals. Right. Now we got to
make a time lock contra
ct here. And this contract is actually
going to be a lot easier. So we're just going to do it from
scratch. So we're going to spdx license I then to fire, my team,
do pragma solidity and let's just do this your point a point
zero, and then we're going to import from open Zeppelin, a
contract called the Time Lock controller. So if we look at the
governance here, if this time lock controller dot soul
contract, and this has all this functionality in here for
creating roles, who can actually propose
who can execute, who's
the Time Lock admin, but it also has these execute stuff in here
as well. It's gonna work in tandem with our governance
contract, right? This is the contract that says that makes
sure our governance contract doesn't just push stuff through
willy nilly. So we're gonna say we're gonna first import that
import at open Zeppelin slash contracts slash governance slash
Time Lock controller. That's all and then we're gonna say
contract, I'm lock is Time Lock controller like that.
And we'll
create our little constructor here, and truck door. And this
because this takes a couple different parameters, we're
gonna take a un 256 Min delay, which are min delay, Min delay
is going to be along you have to wait before executing. So this
is Hey, once proposal passes, great, we gotta wait this
minimum delay, then we're going to do a list of proposers, an
address array memory of proposers. And then the
proposers is the list of addresses that can propose for
us, we're just gonna say
everyone's gonna be able to
propose. And then last, an address array, memory of
executors who can execute everything, and we're just gonna
say executors who can execute when a probe posle passes. And
again, we're just gonna say everybody, and the reason we
need these is because we need to pass these to our time lock
controller, this constructor is expecting three parameters. So
we'll just do Tama controller, delay, pro posers, X EQ, tours.
And that's it. So this is going to be what owns everyth
ing, it's
the timelog, that's going to be owning our box. It's not the
government contract, the government contracts is where
we're going to send our votes and stuff. But at the time lock
that actually everything needs to flow through in order for
governance to actually happen, because we want to make sure we
have this min delay, we go through the right process and
everything. And believe it or not, that's everything. That is
all the code you're going to need as far as the solidity
goes, to crea
te a governance to create a Dao. So even do your
own hard hat compile, make sure everything's compiled. And tada,
we've done it, you've done most of the hard work. Now we're
going to flip over to actually writing the scripts to deploy
and to interact with everything using TypeScript here. At this
point, if you're like, Oh, I already learned everything that
I wanted, I don't use hard hat, I use some other tools. This is
where I challenge you to go out and I challenge you to try
something else. No
w if you've reached this point, I just want
to give you a huge congrats, because you have taken the steps
to build your own doubt build your own governance model.
That's all the solidity that you really need. You can take that
deploy that and you're good to go. But of course, we know that
there's more to being a smart contract developer than just the
solidity you got to do the tooling right to let's go ahead
and we'll jump into writing those TypeScript scripts to
actually do this. And again, if
you want to see a Python version
of this, go check out a link up description to see the Pythonic
version of this. And of course, don't forget to smash that like
button, subscribe, leave a comment on how you're doing so
far. And, of course, give yourself a pat in the back.
Great job. You're doing amazing getting this far. Congrats,
let's jump in. Alright, so we're back here,
we've written our smart contracts already, check wasn't
that easy, right, this was way easier than I thought it would
be. N
ow we're just gonna write our deployment scripts, and then
we're gonna write our scripts to interact with them. Again, my
full repo also has tests, but we're just gonna write some
scripts. And then feel free to check out the tests yourself. So
let's write those deployment scripts to we're actually going
to be using a package for deployment called Hard Hat
deploy. It is absolutely phenomenal for hard hat for
making your deployments much, much easier. We're gonna scroll
down to installation, and w
e're actually gonna go ahead and
install this. So typically, you could install it like this.
Well, we're gonna use yarn, but we're gonna do kind of the more
safe way which looks a little wonky, but I'm explaining it
right. So instead of NPM install, we're gonna do yarn,
add dash dash Dev, and then this whole thing right here. So we're
gonna do yarn, add dash dash Dev, and then just paste that in
here. So this is gonna be at nomic, labs slash hard hat,
hyphen ethers at NPM, colon, hard hat, deplo
y ethers. And
what this is doing is we're basically saying hard hat deploy
ethers is going to be overriding this hardened ether thing. And
we're also going to add ethers as well. And then once we add
this in, check our package json, we can see we have hard hats. We
have hard hat ethers, which is being overwritten by this hard
hat, deploy ethers. And then additionally, we're going to
want to add on a deploy. So do yarn, add dash dash Dev, hard
hat hyphen, deploy. And what this is going to allow u
s to do
is instead of having to write scripts, and do all this stuff,
that kind of makes it hard to save your deployments and
everything, we're going to just create a deploy folder, where
we're going to add all of our deploy scripts in here. So I
absolutely love this package. It's makes deployment really,
really easy. So in here, we're gonna create new file, it's
going to do oh one, we're gonna go step by step deploying
everything, we're gonna call this deploy, govern or token dot
TypeScript, th
at's gonna be the first thing we're going to do.
Also, we're gonna change this to TypeScript. Tada, we now have
TypeScript. Yay. Now the one thing that is kind of nice about
doing kind of that advanced TypeScript thing at the heart
had kind of gives it the beginning, is you don't have to
install all the TypeScript stuff yourself. But we do. So we're
going to do yarn, add TypeScript type chain, vs node at type
chain slash ethers, V five, this is a lot of stuff, don't worry
type chain slash hard h
at, at type slash Chai, at type slash
node. And then we'll make sure this is all Dev, dash dash Dev,
I know there's a lot of stuff. There's all this stuff to make
it TypeScript D. You can absolutely do this in
JavaScript if you want. You just have to do JS files and ignore
the typing. So But feel free to do whatever you want to do.
Alright, cool. And we should be okay here. Let's go into our
governor token here. And we'll create a deploy script. So the
Hardhead deploy GitHub repo has a little de
mo boilerplate code
for you actually do your deploy script. So feel free to
reference here if you lost or confused. So what we're going to
do is we're going to import the hard hat runtime environments,
from our enhanced slash types. And you'll see why we need this
in just a second. And then we're also going to import deploy
function from Hardhead. Deploy slash types. And these are the
two main things you need to create a deploy function with
pothead deploy, we're going to create our function, we
're going
to call it const ploy governance token, it's going to be of type
deploy function. So in order for these to actually work, we just
create a whole bunch of deploy functions that we run with heart
hub. And this is going to be an async function. That's going to
take the hard hat runtime environment as an input
parameter that we're going to call HRV. So when we run hard
hat deploy, which you'll see in a second, we're actually passing
our fake hard hat chain that gets spun up in the backgrou
nd
for us, right, we can even do like console dot log Hello. And
if we do yarn, hard hat deploy. Actually, before we even do
that, if we do yarn, hard hat dash dash help, you'll see,
since we imported hard hat deploy up excuse me, we need to
add this to our config. First in our config, we're gonna need to
do top imports, hard hat deploy top Roscoe need to import at
nomic labs slash hard hat ethers, we're also going to need
to import at type chain slash hard hat. And then we'll leave
it there for
now. We'll have to import more stuff in a second
but literally like that. Now, if we do yarn hard hat that should
help we should see a new task in here. And we do we actually see
a ton of new tasks right? Filled with tasks, check clean compile,
console deployed. And this is the new task that we have that
actually deploys all of our contracts, anything that's in
this deploy folder, our head will go ahead and run right now
in our deploy folder. All we have is this console dot log
Hello. So if we
run yarn, or hit Deploy, we should See, it just
prints out hello. It'll spin up a new blockchain background,
while all of our projects and everything, do some type type
chain stuff, and it says you'll see this a lot deploy script dot
func is not a function. And that's because we actually need
to export this now. So we'll do export default, deploy
governance token. That's why it's getting mad at us. Now
we'll run yarn had her hard hat deploy. And boom, we say hello.
So this is how we can actually
deploy all of our scripts. And
we can run everything that's in this deploy folder in one go,
which is really helpful. So let's go ahead and deploy our
governance token first. And this will get a lot faster as we go
along. Don't worry, so delete this. And first we're gonna do
it, we're gonna say const. We need an account to deploy this
first. So we'll say const, get named accounts, deployments, and
network equals HRA. And this is going to be a little bit more
advanced. This is hard stuff. This i
s this is the slick stuff
we're doing here, we're getting these from our hard hat runtime
environment, which is being updated from hard hat deploy. So
get named accounts is way for us to actually import accounts from
our hard hat config, right into our deploy script. So we're
gonna go to our hard hat config. And we're gonna create a new
config that's a little bit nicer than this. So first, we need to
import the hard hat config type. Since we're using type scripts,
we're going to import hard hat
user config from hard hat slash
config. And we're going to create a config. So I'm just
gonna comment this out. For now we're gonna say const. Config,
it's gonna be type hard hat user config. Equals, and this is
where we can add a whole bunch of stuff. So let's say our
default network is going to be hard hat, which is kind of our
local fake blockchain. And then we're gonna say solidity is
zero, point 8.8, then we're going to do this thing called
named accounts, which is what we came here in the
first place
for. So this is just a list of accounts that we can use. So for
accounts, we'll say Deployer, this will be the name of our
account that does all the deploying, and we'll just say,
default, is going to be zero. So whenever we run an any chain,
our zero with account is going to be named Deployer. What other
thing that we need to do is we need to add our networks here.
And there's actually two networks that we're going to
have, we're gonna have a hard hat with a chain ID of 313337.
And
we're also going to have local hosts of chain ID 31337.
Now, these look like they're pretty much the same. And I
understand that, but they're not. And you'll understand why
in a second, but we need them for now. Just to tell Hardhead
hey, here are the development fake blockchains that we're
working with? Okay. So this is kind of our basic setup here.
Okay. I know that was kind of a lot. But now that we have get
named accounts, we have network, right? Because anytime you
deploy something, it's go
ing to be on a network. And when we
deploy something, if you run yarn, hard hat node, what hard
hat is going to do, it's going to spin up a fake blockchain in
the background. Oh, it's gonna be mad at me for a second. Oh, I forgot to add export, default
config, right, we need to export the config. Hell hard hat that
we're using this version of sling. Now if you're on a hard
hat node, you'll see why we added these two networks up here
hard hat on localhost, we actually get spun up our own
fake blo
ckchain, we get accounts, we get private keys,
we get everything right. This is a hard hat Node running in this
terminal, right? When you run hard hat node, oddly enough,
it's actually the local host network. It's not the Hard Hat
Network. When using hard hat deploy, it's gonna be the local
host network. Or excuse me, when you use hard hat node, it's
actually going to be the local host network, not the Hard Hat
Network. The Hard Hat Network is what it uses when it runs tests.
localhost is when y
ou run this hard hat node and have this kind
of fake blockchain running in your terminal. So that's what
the localhost is. Cool. Well, Ctrl C will kill that. Alright,
great. So now stuff is actually working. So get them to count
deployments is going to be a whole bunch of stuff from
deployments, you'll see in a second and network is the
network that we're on. Okay, great. So now we're gonna grab
const deploy log equals deployments, this deployments
object comes with this deploy function. And thi
s log function,
this log function is kind of nice for doing logging. And then
we're gonna grab const, employer equals await, get named
accounts. So we're grabbing from our config, this deployer
account, right? And it's always going to be the zero with index.
So it's defaulted to the zero index for whatever accounts that
were with. Great. So we now have an account to deploy stuff from
we have a deploy function, we have all this stuff, we're
looking pretty good. Cool. So sometimes they'll do like
a log,
flying governance token, dot, and we'll say const governance
token equals await, deploy, and then the name of the contract
which is governance token, comma, and these are all the
parameters that we're going to pass to it. So we're gonna say
from deployer doesn't take any arguments. So args is just going
to be blank. We're going to do log to be true so we'll get some
logs printed out for us. And then I have this wait
confirmations attribute set in GitHub repo. For now we're going
to ignore
this. But if you follow along with my repo and
you want to auto verify stuff, this is something that you're
going to want to use. So you need to wait some amount of
blocks for this contract to be deployed before you can actually
go ahead and verify it, right. Because if you're using ether
scan or something, you're gonna need to wait like, yeah, you're
gonna need to wait a few minutes. So check out my GitHub
repo for this wait confirmation stuff, I'm just going to ignore
it for now. Additionally
, in my GitHub repo, I have this verify
function, where we check to see if it's on a development chain.
And if it is, we don't verify it. But if it's on like ether
scan or something, we go ahead and verify it. So be sure to
check the GitHub repo to learn how to just auto verify without
having to do anything. Now if we just do this part. And then we
can even do Floyd, governance token to address governance
token dot address. And we'll do yarn hardhat. Deploy. Boom. So
we spin up on a fake Hard Ha
t Network, we do deploy governance
token to address boom. And this is how we deploy a fake has
deployed this to our own little network here. Great. So this
tokens been deployed, we have our deploy script. Yeah, zoom
out a little bit. Let's zoom out a little bit. Great. That's the
whole thing. Now we're going to add one more thing here, we're
going to add something called delegate function. Now, when you
actually deploy this contract, nobody has voting power yet. The
reason is, because nobody has
the token delegated to them, we
want to delegate this token to our Deployer. Right, so we're
going to call this delegate function. So we're going to
create a new function called const. delegate. And it's going
to be an async function. It's going to take a governance token
address as a string, it's going to take a delegated account,
also as a string, so we're racing and say, who do we want?
Delegate? Who do we want to be able to vote with our token?
Okay, so we have created this async function c
alled delegate.
And how do we do this? Well, we say Kant's over its token equals
token equals await ethers dot get contract at. And probably for two ethers for
us from hardheaded Otto did that, which is great. That's
what we want. Once it's like ethers. Thank you vs. Code away.
Ether is like a contract at. And we're going to say we want our
governance token, which is at contract, governance token
address. And I have auto format unsaved, which is why it keeps
formatting like that. This should be
delegated account.
Okay, cool. We have our governance token contract. Now
we can do const TX or transaction equals await,
governance token dot delegate delegate this delegated account
here. And then we can do a wait, we'll wait for this transaction
to be confirmed by one block. And then we'll just do console
dot log checkpoints, wait, governance token dot num
checkpoints delegated account? So what is this doing? So we
have this num checkpoints function which we can go check
to see on that ERC 20
token what this is actually doing. But
basically what this whole thing is doing, we have this delegate
function that we haven't used it. But when somebody calls us,
we're saying, Hey, you can use my vote, take my votes and vote
however you want. And that's what these this delegate does.
Now, if we look at this token, here, see 20 extensions, your
see 20 votes, we look at number checkpoints, we can see how many
checkpoints that account actually has reason this is so
important is because once aga
in, like I was saying when people do
a vote, they do it based off some checkpoints. And anytime
you transfer a token, or delegate a token, basically call
this function move voting power, which happens with the back end,
which writes the checkpoint and says, Hey, at checkpoint x,
here's what everybody has for voting powers. And that's what
these are so important. And I know I said before, it's every
block, but it's actually just every checkpoint, whenever these
checkpoints are updated, that's gon
na be a lot cheaper on gas
than if we just did every single block, right? That'd be kind of
insane. So the checkpoint for this governance checkpoint, and
we'll see what that actually is in just a second. So we'll even
do a wait, delegate, governance token dot address, and Deployer.
And we'll say log delegated. Now, when we're on this
function, John Howard hit Deploy. We have one checkpoint,
which makes sense, right? Because this was just deployed,
it was just delegated. This address has one chec
kpoint.
That's it. And the reason I checked for this is because if
you see zero checkpoints here, it means you haven't delegated
correctly. So be sure to check for checkpoints. But that's it.
We have our deployed governance token contract done. Bravo.
Let's move on to the next one. So what do we want to do after
we deploy our governance token? Well, let's deploy that timelock
or timelock.ts. And we're going to copy a lot of this stuff over
from here, so I'm actually going to Oh, and then sometim
es you'll
get some weird linting errors here. I just do det TS ignore
there. And sometimes you'll get it here too. Oh, actually, we
don't even need network. Okay, cool. Yeah, you don't even need
network loops. Sometimes we get some weird linting errors.
Sometimes Vyas code has a hard time telling understanding like
this, this overwrite thing that we did. So just run that TS
ignore if if it gives you some, some engineers, anyways, yes, we
have this Oh, to deploy time lock here, we're going to dep
loy
our time lock contract. And we're going to borrow a lot of
the things that we did from here, so I'm just going to come
back here are actually going to copy paste these two top bits.
Again, we're gonna do const deploy timelock is going to be a
deploy function, it's going to be an async function that takes
the heart at runtime environment as a parameter, and cool, nearly
exactly the same sorry. And then we're going to grab these first
three lines, or first four lines, I guess, pesos, then
we'r
e going to be getting those exact same things here. And
we're going to be doing nearly the exact same thing. So we'll
do log deploying. Time Lock, point time lock, will do const.
Mark equals await, deploy, unlock. We'll add some
parameters in here, we'll say from Deployer. Now does this
take some arguments? It absolutely does. Right, we can
take a look at the Time Lock, Min delay proposers and
executors. So what do we want our min delay to be? Well, this
is a value that we're actually going to u
se a lot. So what I
usually like to do is I create a new file called helper. hardhats
config.ts. And right at the top, I'll say Export const min.
Delay, and I'll have this delay be whatever I want it to be. So
let's just go ahead and do 3600, which is going to be
approximately an hour, right, you gotta wait this many
seconds, I think that's an hour. Without the old calculator.
There's 60 seconds, in 60 minutes. Great. That's gonna be
one hour. So wait 30 minutes. And we're gonna go ahead and
imp
ort that here. We'll say import, Min delay. Wow,
autocomplete for being great. From helper from, you know, it's
down directory, helper Hardhead config. And that's gonna be our
first argument here. Our second argument is going to be a list
of proposals. Now we're going to leave it blank for now. And also
the list of executors we're also going to leave it blank for now,
we're going to update this in a minute. And you'll see why once
we get there, we'll do log true. This also has a wait
confirmatio
ns thing in my GitHub, but we're going to skip
that for now. It also has an auto verify, we're also gonna
skip that for now. Then we just need to export default, deploy
timelock. And boom, we should be good. So let's try to run this
on hard hat deploying. So now this should run both of these
great deployed governance token deployed timelock. Perfect, we are cruising now
what we want to deploy that governance contract now. So
let's go ahead and do that. We're gonna do Oh, three LOI.
Gov, nor cont
ract.ts. And you guessed it, this is going to
look pretty similar to what we just did. Right. So in our
deployed time lock, let's go ahead and just grab those top
two things. We'll paste it in here. We'll do const deploy,
govern or contract, there's going to be an apply function,
it's going to be an async function, taking a part at
runtime environment, save and close this for now. And we can
even go back and grab these three lines from our Oh, to
deploy time lock, paste that right in here, becau
se we're
going to need to get the exact same things. And additionally,
for this, we're going to need to get the governance token and the
Time Lock contract. So we'll do const governance, token equals
await. Get which actually, we have this, this get function
that comes from these deployments, which literally
just goes out and gets these deployments. So we'll say get
Gover Nance token. And then we also need to get the Time Lock.
So await, get m lock. And we need these to pass as parameters
for ou
r governor contract. Right? Because if we open up the
governor contract that soul, we look at the constructor, it
takes the token, the Time Lock voting, delay voting period and
quorum percentage as input parameters. So we'll do a quick
log deploying go burn nor ello. And they will do const govern
nor contract equals await, deploy, and will deploy the
govern nor contract I'm not sure if I'm spelling this right
always, but that's fine. And we'll do the parameters once
again. So it's gonna be from
Deployer. args are going to be
this list of args. What's the first thing that it needs? It
needs the token first and the Time Lock. So we'll do
governance token dot address, then it's going to need the Time
Lock that address. And it's going to need a vote on voting
delay voting period and quorum percentage. So these are also
values that we're going to make a lot. So let's open back up
that helper Hardhead config, and let's create those as well. So
we'll say Export const just voting period. And w
e'll say
this is going to be five blocks, we'll do export const voting
delay, this is going to be just one block, which I know is
really quick. And then we're gonna need export const Orem
percentage, which we're gonna say is four 4% of voters always
need to be voting. Excuse me, 4% of voters need to have voted for
a vote to pass. Great. So we're going to do voting delay voting
percentage quorum percentage. So we'll import those so to import
voting delay voting period, or quorum percentage from h
elper
Hardhead config. And now that we have those delay period
percentage, we can just do kind of a voting delay, voting
period, farm percentage, and then we'll say log is true.
Again, this one as well. It has a wait confirmations and auto
verification that we're going to totally ignore. And then we'll
export default, blood Governor contract. Oh, Carrie, we're
getting spicy. Let's just make sure this works. Yarn hardhat
deploy. We should see three contracts deployed here. I think
to compile gove
rnance, token deployed timelock deployed
governance contract deployed. Let's go. Alright. Now, we're
not done yet. We have two more deploy scripts to do. The first
one, we're going to call setup governance contracts. Okay. And
this one's really important. The right now our time lock contract
has no proposers and no executors. Right. So we want to
change that we want to only allow for the proposer to be the
governor, the governor contract should be the only one that
proposes things the Time Lock,
and then anybody should be able
to execute the way that this works. We say the governance
contract proposes something to the Time Lock once it's in the
Time Lock, and it waits that period. Anybody can go ahead and
execute it. So Governor contract everybody votes and everything.
Once a vote passes, Governor says hey, Time Lock, can you
please propose this? Tom like goes yeah, sure. But we got to
wait, this minimum delay, once it's been in delay happens,
anybody can execute it. Now this would be
really cool to do an
integration with chain link keepers, by the way, for the
chain link keepers to automatically execute. And I
should build the next anyways. So we have to set this up so
that these work as such, so we're gonna create a new deploy
thing called Oh, four, setup, governance contracts dot
TypeScript. And this is going to be the code that does all the
setting up. And this is gonna look really similar once again,
to all of our other deploy functions. So we'll go head back
from oh thr
ee, we'll paste these two top ones in here, of course,
we're gonna do const setup contracts, is it going to be a
deploy function, going to be an async function, it's going to
take HRV art at runtime environment as parameters, and
then that's the winner right there. Cool. And we're gonna be
grabbing those same three from the top, as you can see, that
gets a little bit easier, because it's kind of repetitive,
right, we're gonna grab that bit right here. And now we're gonna
get those contracts so t
hat we can interact with them. And this
is another reason why a hard hat deploy is so nice, because we
can just do const timelock equals await ethers that get in
the next let's go ahead and import ethers from hard hat
imports. Ethers from Red Hat will even drop a
little TS ignore here. Ethers dot get contract, we want that
time lock contract. And we say we want to attach it to the
Deployer. So whenever we call a function on it, it'll be the
deployer calling that function and we want to do const
Governor
equals await ethers dot get contract, Governor contract,
this is all going to be attached to the Deployer. Right. Now,
we're gonna do log setting up roles. And we're going to set up
the roles right, again, we're setting it up so that only the
governor can send things to this time lock, because the Time Lock
is going to be you can almost think of the Time Lock as like
the president. Right. So everything goes to the Senate,
the House representative, which is the governor and then the
Pres
ident just says yeah, sure. We just got to wait this minimum
delay, but the President will be the one to actually execute
everything which I'm not actually sure that's how it
really works in in politics, but for for now, that's that's where
we're protecting the president or the time like is the only one
that can actually do anything here. So the way that this works
is we're actually going to get the byte codes of different
roles, right? So if you look at these time locks here, so we'll
do ones u
p on contracts. And we go to the governance here. We go
to Tama controller has these things called proposal roll,
executor roll time, like admin, etc. And these are just hashes
of these strings here. But these are these are bytes 32 saying,
Hey, anybody who has this this byte 32 is a proposer, anybody
who has this byte 32 is an executor. Anybody has this byte
there and soon as time like admin, etc. Right now, our
Deployer account is the time like admin, and that's bad. We
don't want that we don'
t want anyone to be a time like admin,
right? We don't want anyone to have power over this time lock.
We don't want any centralized force here. So what we're going
to do is we're going to const we're going to get those roles.
proposer, proposer role, it's going to be a weight, unlock
that row pole sir role. And if you're familiar with multi call,
this would be a great time to do multi call and copy paste that
whole line x EQ tour role at x EQ tour roll, we're gonna copy
this whole line admin rol
e. And this is going to be on lock
admin role. So these are these three roles that we need to fix,
right, and let's go ahead and fix them. So the first thing
we're gonna need to do is we're gonna need to do const row poser
dx equals await Time Lock dot grant roll, propose a roll to
our governor dot address. So saying, okay, Governor, you're
the only one who can actually do anything once you tell the Time
Lock to do something. We'll wait for the Time Lock period to be
over. And then we'll be done
. And then we'll just do an await
posit TX dot wait one block just to make sure. Now we're gonna do
the const executor, TX equals await Time Lock dot grant roll
executor role to nobody, we're going to execute this we're
gonna give this to nobody, we're going to say address zero which
is going to be something that we're going to want to add if we
go to our helper or head config will say Export const address
zero equals zero by you can just copy paste this if you want.
There's a couple other ways
you can do this with ethers as well.
We're just gonna do like this. I like having my conflict this and
then we just import it say import at zero from dot dot
slash helper Harnett config. So we're giving the executor role
to nobody, which means everybody. So once a proposers
thing has gone through, anybody can execute it. So we'll say
executor, TX That wait one wait a block. And then got one more
to do here, we need to revoke role right now our Deployer
count owns that time lock controller, right
. And that's
how we can actually do these transactions, we can actually
grant role because our Deployer account owns it now that we've
given everybody access. And given all the decentralized
access we need, we want to revoke that role. So const
revoke TX equals await. I'm locked up revoke role, admin
role from Deployer will do Rotex that wait, now guess what
anything that's um, like, wants to do has to go through
governance, and nobody owns the time I controller, it's
currently after this runs,
it's impossible for anyone to do
anything with the Time Lock without governance happening.
And then of course, when the export default set of contracts,
great. And then the last step that we need to do here is we
need to deploy the contract that we actually want to govern over
write that box contract, that real basic contract. So we're
gonna create a new 105, deploy box.ts. And we're gonna do some
of the same exact stuff we've done right, so we're gonna grab
these two, these two here, paste it i
n, os deploy box is going to
be a deploy function equals async function, Ah, sorry, hard
hat runtime environment, we're gonna grab those first
three lines, the TS ignore, just like that. And now we're going
to deploy this box. So log, deploying bucks, and we're going
to do const box equals await, deploy box, give us some values
from our Deployer. args does this have any args so open a box
dot soul, I don't see a constructor. And while this is
the easiest contract out of all these though, no cons
tructor,
and then we'll just say log, it's going to be true. And
again, if you want to check out my GitHub repo for that
confirmations, but feel free to do so it's coming here. And
right now, our deployer has actually deployed this right,
not our time lock. So we want to give the boxes ownership over to
our governance process. So now we're going to do say const
timelock, is going to be a weight ethers dot get contract.
The same thing as before. timelock. We're going to grab
ethers from hardhat i
mport ethers from hard hat. And we're
gonna do a little TLS ignore, it's being finicky. And then
we're going to transfer the ownership of our box to this
time lock, okay, and now so this is actually what's known as a
box deployment. So before we do that, we have to get the box
contract. So this is a box deployment object, which doesn't
have contract functions we want to get the box contract object
so we to box or const box contract equals await ethers dot
get contract at box and then we'll just
do you know box that
address. You could also do get contract. Actually both of these
pretty much if you have the address, you can just do box
that address. You could also do get contract here. Either one
works now that we have the box contract, we do const transfer
owner, dx equals await by contract dot transfer TX or
schema dot transfer ownership to our timelock dot address, time
walk dot address, then we just do a weight transfer ownership
TX dot weight one, do a log, you done it. Those are ex
port
default. Deploy box. Oh, let's see if this works. So we just
did everything. We're deploying the GOV token, deploying the
timelock, which owns the governance process, we're
deployed the governance process, we're setting up the governance
process so that it's totally decentralized. And then we
deployed and set up our box so that it only can be updated
through a governance process. Let's see if it works. Yarn,
hard hat deploy. Let's see if it works. Bada boom, you've done
it. So you've just s
et up a script to set this entire
governance process up so you can build your own doubt. Are you
still here? Well, hell yeah, you are, congratulations on getting
this far, we have one more piece to go, we just got to write
those scripts. So we can actually interact with this, we
can actually do a governance, we can actually see exactly what
the governance process looks like. Now again, if you didn't
watch my last video on Dows, be sure to watch that, because
that's going to give you all the cont
ext for this part here. And
if you're still watching 100%, smash the like button, hit the
subscribe, leave a comment in the comment section below, it
really helps the channel out. So proud of you for getting this
far. We're almost there, you're getting now one more to go. And
then you are home free on building your doubt. Let's get
back into it. Alright, so now we're going to
make some scripts to actually interact with propose Q and vote
on anything that happens in our Dao. And these are the scr
ipts,
these are kind of the things that you would do on your front
end when you build this when you build your doubt on the front
end. Or you could do an integration with snapshot or
tally or something like that. And again, if you want to see
come to full functionality on the GitHub, I have this test
flow.ts It's not the greatest test here. But you can also
check this out, because it also does a soup to nuts
demonstration of going through this exact process. So let's go
ahead and start making so
me scripts. So we're gonna create a
new folder called scripts. And this is where we're going to put
all of our scripts. Now the process for this is going to be
we're first going to propose something right? You know, maybe
we're going to propose that our box contract stores the value
77, right? Because when it first gets initialized, it's going to
start with zero. So maybe we'll we'll propose it could start at
77. Once proposing is done, we are voting on it. Right once
proposals it and we're goin
g to vote on whether or not we want
the proposal to go through right yes or no. And then if it
passes, we go to queue and execute, we queue first. And
then we execute, I'm just putting them both in the same
script to make it easier. So let's start with propose here,
because it's going to be the first thing that we're going to
do. So let's create a new function. We'll call it async
function, and then we'll actually export it to export
async function propose. And we're going to be in here for a
li
ttle bit. So let's clear everything out. And okay, cool.
So this is where we're actually going to propose on our governor
contract, right? So we're going to propose a new governor
contract. So the first thing we're gonna need, of course, is
gonna be the governor. So you can't govern nor equals await
ethers dot get contract, govern or contract, right? Since we're
doing ethers, we're gonna have to do import ethers from heart
app. And then we're still getting that fundamental thing.
We'll do a litt
le AT Ts ignore here. Cool. So we have the
governor contract here, we're going to need the box contract,
we're gonna say hey, we want to propose the box contract changes
the store value. So we'll do const box equals await ethers
dot get this is a gift contract, we want get contract, get
contracts at box. And those are the two main ones that we're
going to need just to start. Now, if we look at the proposed
function, right, if we go to governance, we go to Governor,
and we look at that proposed.
This is what it looks like. Now,
I explained this proposed function in my last video. So if
you haven't seen it, be sure to go back and watch it right
because it'll give you everything that you need to know
here are basically we pick a list of targets, which our list
is just going to be just our box contract. These are the targets
that we want to call functions on, we do values like how much
needed ether we want to send, which we're not going to send
anything, we have bytes, a array called data.
So this is going to
be our encoded parameters for the function that we want to
call and then a description. So that's exactly what we're going
to do here. So first, we need to figure out what we're going to
do. So look at Box, we're going to call this store function with
this new value here. So we need to encode we need to encode this
socks here. And we also need to encode what we want to upgrade
it to right so we have to code all the function parameters. So
we'll do const encoded function call
equals box and the way we
can get this box that interface that encode function data and
This is what actually turns it to being this bytes call data,
right? So we're encoding everything and this encoded
function, you can find this in the ethers documentation, we
have to pass it the function to call, and then the arguments we
want to pass, right, and this is how we actually get that. So
let's get these arguments here. So we're gonna say args, we're
going to make our proposed function a little bi
t modular.
So we're gonna say args is going to be an array of anything, and
then we're gonna say function to call is just going to be a
string, right, and then right at the bottom, we're actually gonna
call this, this proposed function. So we're gonna say
propose, and let's say we want to give it 77, the function is
going to be what it's going to be store, store. And that's a
string. And this needs to be a list. So we're going to do like
this. Now, we're actually going to use this all over the p
lace.
So since we're gonna be using this all over the place, we want
to stick them in this part head helper config. So what we're
going to do is we're gonna say, export const, new store value
equals 77. And we're gonna do export const funk equals store.
And I propose, we're just going to import those. So we'll do
import, new store value, and also funk from helper Hardhead
dot config, and it added it in for us, which is great. So we'll
just put new store value in here. And then we'll put funk in
here. And I know this might look a little confusing, but
basically, the first thing we're gonna do is we're gonna call
this proposed function which calls you know, post function up
here. Now we're going to do some fun little then process that
exit zero. And then we're going to do a dot catch error, which
if there's an error, basically, we'll just do console dot log
error, then process that makes it. And this is pretty typical
setup for a really any script you work with, and hardhat. So
great. So
we have constant coded function call, which has this
function to call an arc. So we're basically combining these
into this bytes thing. And we can even print this out, you can
see what this looks like. So you do console dot log, you see that
this is like this, this crazy bytes thing here. And the way we
can kind of test this, we'll do yarn hardhat node, which will
spin up again, our fake blockchain. But additionally,
with Hardhead deploy, it will deploy all of our contracts
here, right, we can
see timelog governance, you know, everything
that we need for testing locally has already been done. So once
we have that up, we can then do yarn hardhat, run scripts,
propose that TS dash dash network, local host, this is
important to do, again, because when you're working with a node
that's running locally, you're going to be working with local
host, we'll see what it prints out here. Contract named Governor
contracts, Governor contract. Let's try that again. And cool,
you can see this is what
that encoded function call and
arguments looks like is this really long byte string. But if
you were to decode this, using the box interface, you would get
the function call and the arguments, which is really
exciting. So cool, we've encoded it to bytes. And now what we
want to do, we encourage the bytes, and now we're going to
create that proposal transaction. So we'll do console
dot log will say pro posing, and say, function to call on box
that address with args. And then we also need to pass
a proposal
description. So we're gonna say pro pose, all description, we
would do it on new line, why not? Pro pulls all this option,
which we don't have yet. Alright, we're gonna get it. So
we also need to pass a proposal description, right, because we
have down here, description. So let's add another parameter to
our propose here. So we'll say proposal description, and this
will be also a string. Down at the bottom, we're going to need
a proposal description as well. So we'll create a proposa
l or
export const proposal description, there's just going
to be some string. So we'll say proposal number one, door 77 In
the box, that's the description here, comma proposal
description. And then we import this or a helper config. Okay,
perfect. So now we have the new store value, the function the
proposal description, and we can now call that propose that we
were just looking at so here's what we're going to do const
propose TX equals await. gov dot propose. And we need to pass
those lists. S
o first is going to be a list of targets, which
for us is just a box that address right only one target.
And again, these these little brackets, make it a list of
values, which is just going to be zero, a list of encoded
function calls or our bytes data, basically, and then the
proposal description, and then we're going to do propose, TX
dot wait one. Now, if you remember, from compound, this is
going to be the exact same if we go back to compound back to
governance, this transaction is literall
y going to be the same
as this created thing here. Right? And if we scroll down,
like more, we can see the code input data, those exact same
things on a compound proposal, right? We have targets values
signatures, well okay, this CES is a little bit more, this a
little bit more advanced here, using signatures, but it's got
the same thing called data and then a description here, okay.
Now since we have a voting delay, people actually can't
vote until the voting delay passes. Now, with a local
blo
ckchain, nobody's actually processing blocks and time
doesn't really pass as quick as we want. And so we're just going
to speed things up for our own testing purposes. So the way I
normally do this is I create this this variable, all
development, exports, on development chains, and I'll add
hard hat and local host. Because we can actually do things with
our own local blockchain, we can actually speed up time, we can
speed up blocks, we can do all this crazy stuff. So usually,
I'll actually impor
t this in here. And then we'll do a quick
F. And I'll say, if we're on a development chain, let's just go
ahead and speed things up for us. Right. So I'll say if
development chains that includes network dot name, and then we
can import network from from ethers, as well. And this is
what I was talking about those super six skills that you're
gonna learn. If it includes network dot name, then we're
gonna go ahead and move the blocks forward, right, because
if we're not on development chain, we can
't actually move
blocks. So what we'll do here is actually I'll create a new
folder called utilities, you folder, you are utils. And in
here, I'll create a script called Move blocks.ts. And we're
just going to create this little function called move blocks,
which moves blocks for us. And you'll see how we do this. So
we're going to import network from heart app, because we want
to speed up that voting delay, we'll do export async function,
we'll call it move blocks. And it'll take an amount, whi
ch will
be a number, so how many blocks that we actually want to move.
And then we'll just do console dot log moving blocks. And we'll
say for let index equals zero, index is less than amount. index
plus plus, or we're going to do is we're going to do a weight
network DAP provider dot request request. And we're going to
request method EVM. Mine. So basically, we're mining for our
local blockchain, right, so you can find these docks in the hard
hat docks in the ethers docks, there's a couple diff
erent
places you can find these. But this is kind of this really cool
hack that we can use to actually move blocks forward on our local
chain. Now, obviously, this won't work on an actual chain,
because you'd actually have to do the mining but on our local
chains, we can absolutely do this. So we've exported this
move blocks function, and we're actually gonna grab that Mark
propose, we're going to import move box from utils blocks. And
we're going to say down here, if we're on this development c
hain,
and do a weight move blocks, and then we'll move blocks by that
voting delay that we were talking about, right? Because we
need to wait that voting delay in order to move and it looks
like an auto import it for us from our helper hardware config.
I told you, we were gonna use that a couple of times, but
we're gonna move blocks by that voting delay. Now, this proposed
transaction does some stuff that we actually want, right? So one
of the big things that it wants is, is it has this proposal
ID.
And if we scroll down to the event that it emits, it ends up
emitting this proposal ID, we actually need the proposal ID
for later on when we actually go to vote. So what we're going to
do is we're actually going to do const propose receipt equals
proposed text dot weight. And we're going to get those events
from this proposed receipt. So we're gonna say const. Proposal,
id equals proposed receipt, that events, the zero with event
because that first event is the only one we care about that
are
your stop proposal ID. This is how we get this proposal ID from
this omit also created event. A couple other ways to get
proposals to get events in hardhat. And I got a video on it
on my YouTube, if you want to go check that out. Something else
that we want to do is maybe we want to see what the deadline is
with a snapshot, you can go ahead and check my GitHub to
kind of see what the snapshot looks like or the deadline,
basically, how long we have to vote, you know what snapshot
you're worki
ng with, and all these different things. But for
now, we're just gonna stick with the proposal Id be sure to check
out the GitHub for more stuff. Now we want to save this
proposal ID, we want to save it somewhere so that our other
scripts so that our vote and our queue and execute know what this
proposal ID is going to be when we run those. So what we're
going to do is we're going to create this file called
proposals dot JSON. And it's going to store all of our
proposals. So we're gonna say pro
poll, souls dot JSON, it's
going to have all of our proposals in here. So what we're
going to do and we're going to add this to our helper Hardhead
config, and do export const, proposals, file equals proposals
dot JSON. And we're going to import this at the top as well
as proposals file. And then once we get this proposal ID, we're
going to do is we're going to read all the current proposals.
So we're gonna say let proposals equals JSON dot parse, and we're
gonna do Fs dot read file sync from th
is probe proposals file,
and we're going to pass it to UTF eight. Now, this Fs we don't
have yet, so we're going to need to import Fs. So we're going to
do yarn add Fs like that. If we're looking at package dot
JSON. Oops, I should have saved that as Dev, but I didn't Oh,
well, doesn't really matter. Now that we have that we can
actually import this into our TypeScript. So we're just going
to say, import star as Fs from Fs. And now we can actually use
Fs F as a way to kind of read from files. No
w we can get this
list of proposals. So we got two proposals that JSON right now
we're just making a blank JSON. So the first time we run it,
it'll just be blank. But later on, it'll have stuff in it. And
the way that we're going to save these proposals is we're going
to say pro proposals of network dot config dot chain ID, and
this little bank to say, yes, there will be a chain id.to
string, we're going to store them by their chain IDs, right.
So for each network that we have a proposal to we'l
l we'll start
like that. And then we'll do dot push proposal, id.to string, and
then we'll write it back. We'll do Fs dot right, file sync,
proposals file. JSON does string phi, proposals, and awesome. And
that's all we need to do. So let's go ahead and actually run
this. And then I have some console dot logs in here saying,
Hey, here's what the proposal state is, is it open? Is it
voting is it canceled, you know, etc. What the proposal snapshot
is, you know, again, check my GitHub for that. But
what we can
do, now this is done, we can do yarn, Hardhead node, spin up our
little note here. And all the contracts are deployed. And then
we'll do yarn, run scripts, proposed that TS dash dash
network, local host scripts proposed that TS is not found.
Pro pose, yarn, hard hat, run scripts, pose IDs, oops, dash
dash network, local hosts. Try this again. Now you found it.
Yay. Oh, I ran into an issue. Although description Moviebox
Cannot read property zero of undefined. Let's see where it
got m
ad at me. Poser sheet dot event. It is events, not event
shots again, on the localhost. There we go running ran into
another issue. Posle already exists. Okay, so great. So the
proposal already exists, let's go ahead and just like kill the
node, restart the node, so you can't have two proposals that
are exactly the same, basically, right. So we can't do that, we
would need to change the description or something. So
we're just going to kill the node and restart. Now we're
going to run this propos
al again. And hopefully this one
should work this time. Community Property zero of undefined owes
receipt. This needs to be await because it is a promise, kill
this one more time we run it once all these get deployed. And
we're gonna go ahead and run this. Now you can see how much
quicker this is than if you were to actually send this to a test
net right to an event a lot of waiting, which is no bueno. And
we run into one more Cannot read property, push them undefined.
That makes a lot of sense,
too. Because proposals dot JSON as
nothing for chain IDs. So 231337. And we'll put a little
list in here. Right now it's an empty list. Now we're going to
kill this one more time, kill it. And then once this goes, then
we're gonna go ahead and do this. Perfect. Now we're going
to run this and now it should save and everything should be
peachy hunky dory. Awesome. proposal number one store in the
box, we move the blocks. And if we look at proposal dot JSON, we
now see there's a list of proposals
. And this is the
proposal ID of that one we just created. Oh, okay, we made a
proposal. Awesome. Nice work, we'll leave that Node running.
And hopefully we'll just do things right for the voting. So
let's create this vote script now. Okay, so now we proposed
it's time to vote, let's do a little voting. So this is going
to look pretty similar to that script, we just we just created
right. So we're in New async function, we'll call this main
proposal index number, and we're calling this main beca
use we're
going to have the vote function be a little bit different.
You'll see why. And at the bottom, of course, we're going
to do main index dot then process dot exit zero, catch
error arrow function, console dot error, error, and then
process that exit. One main index, we're gonna say our index
zero cost index zero. We'll do like this cost index is your
postal index. So we're gonna get we're gonna get that bit zero
with index, right, the first index in our proposals dot JSON.
So whatever is
the first one in this list is what we're gonna
use, right? And that's what we want right now. There's only one
so it's easy. So we're gonna get that first one in here. So first
thing we're gonna need to do is we're gonna need to grab the
list of proposals. So we're gonna do const proposals equals
JSON dot parse, FS dot read, file sync proposals file, UTF,
eight. So of course, we're going to need to import a bunch of
stuff. We're going to import proposals file from that a slash
a Bernhard config.
And then we're going to do import star as
Fs from us. Great. We have Fs, we can read stuff, and we can
get those boats. Okay, cool. So we have a list of proposal IDs.
Now let's get our proposal idea we'll do const probo. also ID
equals proposals of network dot config. And then that's not what
we want. We want to actually import network, import network
from hard hat, network dot config, that chain ID,
exclamation mark of proposal index, which for us is going to
be zero, right? We're getting that
first proposal in the list
of proposals. Now we're gonna choose how we want to vote. So
zero equals against one equals four, and then two is abstain. I
don't know why you'd ever abstain abstaining costs gas,
you could just not vote, but we're gonna say const vote way
and the way we're gonna vote equals one. And we can also do a
reason. So if we go back to our governor, there's a couple
different functions we get to do to vote, there's cast vote,
where we just cast a vote, cast, vote with reason
, and cast vote
with signature where we actually do a signature. And I asked this
question, Hey, what is cast vote by sick do on the opens up on
forum, I was like, hey, what's the what is the purpose of this?
My hunch was that anyone could then execute this vote on behalf
of me if I didn't send the transaction. And that's exactly
what it is. This method implements a meta transaction
and allows a project to subsidize voting fees, the
voters can generate a signature for free, and the project can
s
ubmit those and pay for the gas. So this is incredibly
powerful. And this is the function that allows this, this,
this cast vote by signature is what allows that snapshot chain
link integration that you know, hopefully one of you build, but
for us for this, since we're not implementing these meta
transactions, these off chain stuff, we're just going to do
cast a vote with reason why because we want to give it a
reason. That's really it. So we're gonna say const boat TX
response equals await. Gov
nor dot cast, excuse me, we need to
get the government contract. So we'll do const. Gov nor equals
await ethers dot get contract. Gov contract. And then if we
don't have ethers, we should get ethers. Great. Let's get ethers.
And we'll ignore that. But now we have the contract. So we'll
do a weight gov dot cast vote with reason spell this right?
With reason. And we'll say proposal ID that way. And then
the reason which we don't have a reason here, so let's make a
reason. Let's say the reason is
reason, equals I like do cha
cha, if you know that film, you should definitely comment it in
the description. We have a reason so we're voting for we're
saying yes, we want we do indeed want you to change the box to
77. And the reason is because I like a do the Cha Cha makes
perfect sense if you don't think about and then we'll do a wait,
Kotex response that wait. So I do some stuff, again, checking
the state of the proposal where different numbers mean, hey,
it's in process, it's voting, etc, w
e could check on that. But
we're gonna skip that for now. All we're going to do now, now
that we voted, we're going to be the ones to vote. So we're just
going to once again move the blocks along. Why because we
want to just get to the end of that voting period. So we're
gonna do again, if velopment chains, that includes network
dot name, then we're gonna do a weight move blocks, voting
period, plus one. So we need to import a whole bunch of stuff in
here. poses file development, looks like thos
e got auto
imported, we need to import this move blocks. So import move
blocks from utils blocks, and that network is in here. And
then we need voting period from our helper config. voting
period. Okay, cool. And then we'll do console dot log, voted,
ready to go. Now the reason that I checked the proposal state is
because there's this state function in the governor
contract. So if we look up state, what this does is it
tells us what the state of the proposals in right if it's been
executed, retu
rn that's been executed, if it's been canceled,
return has been canceled. You have the deadline, check to see
if it's active, check to see if quorum reached all this stuff,
right? And what you're usually looking for is worm reached and
vote succeeded, right? If both of these happen, the proposal
state DOT succeeded, right? Otherwise, it's defeated or it's
not there yet. I believe this is a one and this is a zero. So if
you were to call that function and get the state right, now we
should get a z
ero. Or excuse me, we should get a one for this
having passed. If you want to do that as a little extra credit,
feel free to do so. So let's see if we did this right. Well,
actually, I guess we got to change this just to vote. But I
just wrapped everything up into main. So let's see if this
works. Yarn, hard hat run scripts vote network, local
host. So we should get a little console dot log at the bottom
that says voted ready to go. So now we're voting. We didn't just
go to the Arctic Council, w
e could do yarn, hard hat console,
dash dash network, local host. And in here, you can actually
just check the state right in here. Why not? We'll copy this
line as governor because we either get contract Governor
contract. Now we can do a wait, Governor, that's state of go to
the proposals dot JSON, grab this, paste it in here. And we
get a for the state of this right now is for I forget what
four means. It's like a proposal state. The proposal state is
actually in the eye governor so the inter
face of the governor We
can see, zero is pending, one is active, two cancelled, three
defeated, and four have succeeded. So we are in a
succeeded state, which is really good. That's exactly what we
want. So let's go ahead and quit now. Excuse me Ctrl. C, our
proposal is now in a succeeded state. And we've actually moved
the blocks along the voting period. So voting is now over
because we cheated. So now let's go ahead and queue and execute
this to the last bit here. So this is gonna look real si
milar
to what we've done already. Right? Let's minimize this,
export async, function, Q, and execute. And then at the bottom,
we'll just call queue, and execute. And I'm just going to
copy paste, but it's that same syntax here, then process exec
catch, blah, blah, blah, you get the drill. So in order to queue
and execute, go back to the governor contract, not the
governor, let's go to the governor. First thing we're
going to do is call this queue. Now this Q function is actually
in the governor
time lock, which is in this extensions here. So
we can find the governor Time Lock controller here, and it
does exactly the same as propose we take everything that we did
in the proposal, and then we just queue it like so. So we
pass the exact same values here. And that's so cute. So what
we're going to do is we're gonna need to first get those exact
same values, which I told you we're going to use a few times.
So we're going to import bunk, new store value, proposal
description of our article t
hing, right now that we have
all that stuff, we'll say const args equals a new store value, we'll do const box equals await
ethers dot get contract, let's get that box contract again,
we're gonna have to import ethers from our hat. And this is
going to be from we're gonna do TS ignore, then we're once again
code this function call to const encoded function call equals
Boxtop interface, encode function data. Once again, we're
gonna do funk is the function we want to call and args this is
like rea
l similar to the our proposed bit that we did. And
then we're gonna do content description hash equals ethers
dot utils that get GAC 256 ethers dot utils, that to you,
TF eight bytes, this will make sense in a second. We're also
description. So what's our propose, all we did was pass our
proposal description, however, it actually gets hashed on
chain, and that's what our view and execute is gonna be looking
for, it's gonna be looking for the description hash, instead of
just the pure description
, right, and it's gonna be a
little bit cheaper gas wise, which is good. So now that we
have the description hash, now that we have all the same
functions that we did for the suppose it's time to queue them.
So do cost gov equals await ethers dot get contract. Gov
contract, console dot log will say we're queuing and then we'll
do cos Q, dx equals gov dot q. And we're going to pass the
exact same parameters we did with the pose except for what
the hash instead of the actual proposal, so box that
address
zero for eath, and pass that code at function call. And then
the description hash rate. And then we're going to do oops, is
going to be a weight here. And then we're gonna do a wait. You
text Oh, wait, one, wait a block there. And great, then we're all
queued up. Now we still have to wait that minimum delay, right?
Remember, on our timeline, it's got this min delay thing. It
says, Hey, once something gets queued up, you can't just
execute it right away. You gotta give people time to get
out. So
we're going to speed up time again, we're going to say if
development chains develop l meant chains that includes and
then it looks like it auto imported for me development
chains. Yes, it did. Amazing. That includes network dot name.
Let's just make sure we import network from Hardhead. That
includes our name, then, of course, we're going to move
blocks. But we also actually have to move time here as the
minimum delay is looking for some time. So let's create a new
util called Move time
. Okay, move time that Yes. And this
util is going to allow us to move time so you're learning all
the cool stuff. So we'll quickly write a script to do this
important network from hard hat export async function move time,
how much time will be a number of Mount console dot log, moving
time, and let's say await network dot provider dot send
EVM increase time and then just by the amount, then we'll just
say console dot log, move forward, amount seconds, it goes
forward and seconds. Cool. So now w
e have this move time
function. That was pretty quick, right? So first, we're going to
move time and we're gonna move time by that min delay first,
plus one just to be safe. And then we're also going to move
blocks. We're gonna do a weight move time, and then a weight
move blocks. And we'll just move on block. So got it Import move
blocks from utils. It imports move time from those utils as
well, time. And then we also have to import this min delay,
which we get from our helper. Hardhead config.
Great. So we
moved all that stuff. Again, if this were a real chain, you just
have to wait. But since we're not a real chain, we can do
whatever we want. Yes, love doing whatever I want. Now that
it's all cued up, the voters passed, we're looking spicy.
Let's drive this home. Xe cuting do a little console dot log
executing the const execute TX equals await. gov dot execute.
We're going to pass this the exact same set of things we did
for the Q TX so I'm literally gonna copy this. paste it down
here. And then we're just going to do a weight SQ TX dot weight.
We're going to wait one block. And then the final hour we'll
see if the governance updated our box contract const box new
value equals await box dot retrieve. And that will do
console dot log. New box value. Box new value.to string Oh, so
if we did this right, it's new box value to be updated. Let's
see if we did it right yarn hard hat run grips Q and execute now
work localhost. Did we do it right? Are we successfully done
governan
ce? We did. That's okay. We're gonna figure out what we
did wrong. Did you mean could check 256 I spelt some stuff
wrong. It needs to be spelt like no CK. Just just k. Okay, let's
try again. It failed before it actually did. I think that's
good. Queuing moving time provider EVM and crit cuz I
spelt increased time wrong. Oh, no EVM increase time to just
double check. Make sure I'm spelling this right T is
actually capital. So I totally messed up. So it's actually
already been queued. It's been qu
eued right now on our little
node EVM increased team. So we can either delete and kind of
restart, or I can just go ahead and I'm just going to comment
out a bunch of stuff, we're just going to skip the cueing here.
We're gonna run this one more time, because it's already been
queued. And now should just execute, we're gonna move time
again. But that's fine. Oh, my goodness, we did it. Right. And
then normally, you would just do it in one script. But this queue
would fail because it was already
queued. Right, you can't
queue twice, move forward. In time, we move blocks we executed
and we got a new box value completely using our Dow
completely decentralized voting completely on chain, no third
party trust going in on here. There's no voting booth, there's
no you know, spending 1000s of dollars on staff, everything we
just voted on happened right in front of our faces. Now, again,
I highly recommend go to my GitHub repo, you take a look and
you see what's going on here. Again, if you wan
t to see
JavaScript stuff, feel free to do some JavaScript stuff. But
this goes over how to just get clone and get started if you
want to do that as well. But if you walked with me here, if you
walked through this with me, you have learned in the absolute
Khan, thank you so much for being here. And I'll see you
next time. All right. Now, welcome to the
final section of our course, the security and auditing section,
this one is going to be a little bit less coding and a little bit
more explaining
. And most of what we're going to be learning
about here is in this Hardhead security FCC section. Throughout
this course, we've given you a couple of tips about different
security features. One, we talked about reentrancy, we
talked a little bit about Oracle Tax, and we're going to talk
about those more. And some of the tools we can use to make our
code more efficient, to look out for bugs, and to make our code
more secure. So we're gonna go ahead, we're gonna go over this
hard hat security FCC
code base, and we're going to walk through
it a little bit. So one of the first things that we're going to
talk about is, what is an audit? Well, an audit is going to be a
security focused code review, looking for issues with your
code. So for example, let's say we have some code that looks
like this, this should be a little bit familiar, because we
talked about this in one of our earlier sections with
reentrancy, our code, withdraw, goes and sends ether and then
updates the balances, this code
is clearly vulnerable to a
reentrancy attack here. And this is something that an auditor
would catch. Since when we deploy our code, that code is
immutable. And that code will always be there, it's really
important to have these security reviews done before we deploy
our code to a main net, and before we go live, so if you're
going to deploy some crazy, massive defy protocol, and
you're gonna have billions of dollars of people's money locked
into your protocol, you probably want to make sure th
at the money
is going to go to the correct places. So audits are incredibly
important for the lifecycle of our projects. And we want people
to peer review, we want people to review our code to make sure
that everything looks good. Now, when we send our code to audit,
though, we shouldn't just say hey, here's our code. Can you
check them Make sure it's good. That's not going to give an
auditor enough information, they need to be able to very easily
know what your code does, how to work with it, a
nd what you're
looking for. Because auditors aren't going to be kind of this,
this failsafe, where if your code is terrible, they're going
to catch everything. Auditors are human beings too. They can
miss things as well, auditors. Also, don't make sure that your
code is bug free. Like I said, audits are security focused peer
reviews for your codebase. And when you do send your code to
audit, you want to make sure you help out your auditors as much
as possible. There's an amazing tweet thread fro
m Tinto and
previously was an open Zeplin auditor with a ton of tips and
tricks for working with auditors, I highly recommend you
pause the video, you click this link and you read through his
tweets because they are fantastic. openzeppelin has a
readiness guide to try to help you make sure that you're even
ready for an audit in the first place. And we've got a link to
this readiness guide in the GitHub repository. The summary
of them are to add comments to your code, use natspec, which we
learne
d about to document your functions, document your
functions, document your functions, test, be ready to
talk to your auditors, and be prepared to give them plenty of
time. They are literally pouring themselves over your code for
weeks on end to make sure there's nothing wrong. If you
rush your auditors, you're gonna get a rushed audit, and they're
going to miss things. So let's talk about the auditing process.
In auditing process is going to look like this. First, they're
going to run your tests
. That's the first step in order is
always going to take and right there, they're gonna find okay,
do they have enough code coverage? Is everything passing?
What do the tests do? What is the optimal functionality, after
an auditor runs tests, they're going to read specs or run your
docs. And then they're going to run some fast tools like
Slither, linters and static analysis. And that's going to be
one of the first things we're going to talk about slither and
static analysis. So static analysis i
s the process of just
running some program to read over all your code and look for
commonly known bugs. One of the most popular static analysis
tools is going to be this tool called Slither. And that's going
to be one of the first things we're going to do here. So let's
go ahead and open up our VS code now. And we'll make a new
directory called hardhats security, FCC. We'll cd into it.
We'll do code period. And we'll open this up. Now what I want
you to do, instead of starting a new folder, and
everything is
we're going to get clone, my heart had security FCC. So we'll
do git clone, our net security FCC space, and then put a period
to clone it into this directory. And we'll get everything like
this. Now in here, this comes with a couple of different
contracts for us already, that each have a different
vulnerability, one of them is going to be bad RNG. This is a
contract that picks a random winner of a raffle using block
difficulty and message dot sender. This isn't truly random,
as the
miners can influence the block dot difficulty, and people
can cancel transactions. And there's a ton of ton of
different vulnerabilities with creating randomness in this way.
We also have this liquid pool as an Oracle, the two most common
types of attacks are reentrancy, which we've learned about an
Oracle manipulation attacks, which luckily for you, we've
taught you about decentralized Oracle's and working with chain
link, which should make you a lot safer. And especially for
this section, I'm
going to harp on these, please, please,
please, if you taking this course, please do not make a
protocol that falls victim to one of these, I will feel like I
have failed you. If you build a protocol where you use some
centralized oracle that gets manipulated, or you build a
protocol that has a reentrancy attack. The tools that I'm going
to show you right here are going to help you with reentrancy. And
everything I've taught you about chain link should hopefully
teach you how to not get Oracle
manipulated. So in this contract
here, we're using a liquidity pool as an Oracle and this is
kind of some advanced defy stuff here. This is a minimalistic
decentralized exchange example where people can buy and sell
and swap different assets. Now using this singular exchange, to
get the swap price is a terrible idea. Because this is a single
protocol for a single price. The price from this protocol is a
single centralized location, and we don't want to get our price
from a single centralized exc
hange. We want to get it from
many exchanges. Getting the price of any asset from a single
decentralized exchange is not decentralized, is somebody
manipulates the market doing some crazy advanced defy things
that will ruin the price of your assets. So getting the price of
your assets from a centralized location is a terrible idea. We
have a metamorphic proxy here. The issue here is that it's
initialized double, and we don't guarantee that the contract has
been initialized. We have a classic ree
ntrancy issue here.
And then we have and then we have a vault here where some
password is stored on chain and we're crossing our fingers that
nobody reads this password to unlock it. So we're going to run
some static analysis on these contracts, see if that static
analysis can spot some of the bad things in here. To get started, we're going to
use a tool like I said called slither slither tool was created
by this Crytek team, aka the trilobites team. Now, trilobites
is one of my absolute favorit
e auditors in the space. And I
absolutely love all the tools that this team puts out, they
put up open source security tools for any of us to use such
as slitter. Now to get started with Slither, we actually need
to install Python first. So you can also run it with Docker. But
I'm going to show you how to how to work with Python first. So if
you haven't worked with Python, before, you can come to
python.org/downloads and download Python right from the
website, you'll know you've done it right. A
nd you can run python
three dash dash version, like this. Or if you have an older
version of Python, you can run Python dash dash version. Once
you install Python, you should also have this tool called PIP
three installed and you can check by running PIP three dash
dash version, or PIP dash dash version. And we also want to
install this sock select package just in case we're using weird
versions of solidity to install. So select, we run PIP three,
install sock select, like that. And then we can
do sock, select,
use. And then we can choose the version of solidity or slither
to work with. Once you have those tools, you can just run
PIP three install slither analyzer like so. And you can
install slither into your Python environment. I'm not going to
run it because already have. You can also learn how to do this
all with Docker and we'll learn how to do this with Docker in a
little bit. Now in our package json, we actually have command
script in our package. JSON for running Slither, you'l
l know
you've installed slither correctly. If you can run
Slither, dash dash help you get an output like this. Now we can
use slither to run it on our contracts folder by running this
big command here. So we'll say Slither. And we want to run it
on dot slash contracts, we need to tell it that it has some
psaltery mappings, and every time it sees open Zeppelin, it
should use Node module slash open Zeppelin and every time it
sees chain link introduced node modules slash chain link. And
I'm just go
ing to read from our package json. And we're
excluding a couple of functions that it runs and excluding
builder ignore, but don't worry too much about that. We've
actually just run that by first running yarn to install all of
our packages. And after we've installed all of
our packages, we can run yarn Slither. Or you can copy paste
that slither command and run it directly. Now we'll get this
massive output that looks like this with some red and some
green. Let's go through what's actually happen
ing here, the way
that we can read Slither, it'll list out a number of lines that
have an issue and then a reference to that issue. And
each one of these is separated by a new line. So that's a
section that's exception, etc. So if we get a red here, that
means that there is a high impact issue that we definitely
should address. And it even comes with a reference link that
we can copy paste and put into our browser and see what the
issue is and more information from the slither tool about what
th
at issue is and how to correct it. We can see it catches our
metamorphic contract issue. It says metamorphic contract is
never initialized. It is used here in metamorphic dot kill.
The reason that this is a massive issue, if we go to our
metamorphic contract outsole is that if we deploy this contract,
somebody else could initialize this code, become the owner, and
then automatically kill it before we even have a chance.
This is actually something that has happened in the past and has
caused a to
n of issues. So if we see red in the terminal, this
means Hey, massive issue, we should absolutely check it out.
Now there's gonna be a ton of green in here. These are
detectors that are probably low impact, and they're probably
okay. And in fact, we can see, it's even just calling out some
opens up on stuff here, saying, Hey, we see some inline
assembly. Inline assembly is kind of scary, maybe don't use
that. So you can think of green as kind of a warning that
there's a low likelihood that this
will impact anything, but
you might want to check it out, we get this different versions
of solidity used, which is just saying, hey, there's a couple
different versions of solidity that might be something you want
to keep in mind, maybe you should use the same versions of
solidity. We have this allow old versions. And this is actually
why throughout this whole course, we've been using zero
point 8.7 Because zero point 8.4 and zero point 8.7 are
considered more stable versions of solidity. So i
f you're using
versions outside of there, so there will say hey, maybe you
want to work with a different version. We have some flags in
here about maybe Hey, you should make a variable constant because
it never changes which is great. Uses literal with too many
digits saying hey, this Just kind of hard to read, maybe you
screwed up some of the zeros, loud old versions. And what's
this reentrancy in ether store dot withdraw. So just by running
this slither tool, we can catch a reentrancy vulnerab
ility in
one of our contracts, which is fantastic. So running the static
analysis caught at least two huge vulnerabilities in our
metamorphic contract. And in our reentrancy contract, it didn't
catch the issues involved at sole liquidity pool, or bad RNG,
though, which is why we don't only want to rely on slither
because it's not going to catch everything, but it will catch a
lot of major vulnerabilities. So that's how we can use Slither,
at least from a middle middle stack point to get started.
So
great, we just learned how to work with Slither. That's one of
the first tools that are really fantastic in our audit process.
And that's going to be considered a fast tool for
static analysis, running tests, linters, etc, are also types of
static analysis. After we run a tool like that,
we enter some manual analysis where we walk through the code
ourselves manually, and maybe we do it in tangent with running
some slower tools, like a kitna Manta Corp, and other symbolic
execution tools. Sym
bolic Execution is where we simulate
executing transactions on the blockchain. And one of these
symbolic execution tools that we're going to work with is this
a kidmin tool. Again, this is a trail of bits tool for doing
something called fuzz testing. Now in programming, fuzzing or
fuzz testing is an automated software testing technique that
involves providing invalid unexpected or random data as
inputs to a computer program. In a lot of our code, oftentimes,
we're going to get people interacting
with them in ways
that we will never think about. So we want to be able to provide
random data and random information to our test to see
if something weird happens that we weren't expecting. So we can
actually build our own fuzz tests in our hard hat projects
and run these fuzz tests, I've actually created a sample of
fuzz tests, we write our fuzz tests in solidity, actually, as
opposed to writing our tests in JavaScript. So let's say for
example, we've built this vault contract. And we think t
hat at
first glance, hey, nobody should ever be able to know the
password, and no one should ever be able to unlock this contract.
Which obviously, we know is ridiculous, because we know that
anybody can read anything in a storage variable. So we know
that this should fail, but it might be hard to write a test.
To catch that this actually would fail. A good approach to
testing this would be to just send a ton of random bytes 32
objects to this unlock function to see if we can unlock it. We
can w
rite a fuzz test to do exactly that. So in my vault,
fuzz test dot Sol, we're importing vault outsole. And so
we're saying vault fuzz test is vault and we have a password of
123, ASD 123. And now we have a function called a kid and a test
find password, where it's going to send a ton of random data
into vault to try to make s locked equal false. So we just
say s locked equals true here, and our first test will try to
make s locked equals false. Now we could install just the kitna,
but at this po
int, it's a good idea to bring up our the
security toolbox from trail of bits. So trail of bits has a
package called the eath security toolbox, which has all their
security tools in one single container kitna, Ethan o Manta
core, slither, rattle, and not so smart contracts, it has all
these in the same exact package. Now to work with this toolbox.
We're gonna need Docker installed. So we're gonna do a
little bit of installation here. And again, sometimes this can be
the hardest part of the cours
e is just installing these
packages. So we've left a link to Doc's dot docker.com, get
Docker to install Docker, to actually work with these tools,
you're just going to come you're going to click whichever one of
these is appropriate for you to install Docker, once we have
Docker installed, we can run the E security toolbox by pulling it
down from the Docker equivalent of GitHub. And we're going to
use a whole bunch of Docker commands that I'm not going to
explain here because this isn't a Docke
r course, if you're
looking to get into the security stuff, I would definitely
recommend reading up on all these commands afterwards. And
we're going to leave a ton of links for you to learn more. And
in the package that JSON associated with this lesson. We
even have the command to get set up right in here. So we can just
run yarn toolbox, which will run our Docker command like this. So
I'm just going to run yarn toolbox. And if you get
something like this saying cannot connect to the Docker
dae
mon is the Docker daemon running, because I need to have
my Docker daemon running. Since I installed Docker desktop. I
need to have my Docker engine started and running for it to
actually be working. Again to work with this. There's a lot of
Docker setup and configuration that needs to happen, which I'm
going to leave a ton of instructions on how to get
started with Docker. Once we have Docker setup. Now we can
run yarn toolbox, which will stick us into a new shell to
work with any of these tool
s that trilobites has Out of the
box. Now our vault fuzz test comes with a config as well.
This is in a Yamo file with all our arguments for running a
kidnap. So it has a test limit, which is how many different
runs, we should do a time delay, block delay. And then of course,
some re mappings in here. This darker shell will already have
the security tools already installed like the Kidner test.
So we'll run a kinah test on SRC slash contracts slash test slash
fuzzing slash fault fuzz test dot So
l dash dash contract will
be vault fuzz test, dash dash config will be SRC slash
contracts slash test slash buzzing slash config dot Yamo.
And we'll go ahead and we'll hit enter here, and it'll say
analyzing contract, it'll give us an output like this, it will
give us an output that looks like this. What it's saying is
it found a use case where it could make s locked equals
false. And the use case was 123, ASD 123. So when what seemed
like almost seconds, it found the password to unlock our
cont
ract. And this is why running a fuzz tester can be so
powerful, we thought our contract was secure, but it
immediately found the password, which means anybody else could
immediately find the password. And this would be an indicator
that what we're doing there is not a good setup. So we'll hit
CTRL C to escape. And to leave our Docker setup here, we'll just write exit. Now
again, I'm going to leave a ton of links to work with a
kidnapper and work with this fuzz tester in the GitHub repo
associate
d with this lesson, so that you can go ahead and learn
more. Now, if you take anything away from this whole section, it
should be this right here. The two most common tasks are
reentrant. See, and Oracle manipulation. So if you're not
going to be an auditor, and you just want to deploy things to
main net, always, always before you deploy anything, the
absolute minimum that you should be doing is always running
Slither. And then looking manually for Oracle manipulation
and reentrancy attacks. If
you see in your code that you're
getting pricing information. Price is a piece of data that we
as humans have assigned to something if you're getting
pricing information from a centralized location, rethink
that scenario, rethink what you're doing there. If you're
getting a random number, if you're doing any type of
automation from a centralized location, rethink it and change
your strategy. The chain link Oracle network has been created
for a reason to prevent getting hacked like this. So pleas
e keep
these in mind before you deploy anything to main that with any
type of security guarantees. Okay, great. So we've learned
about the fast tools, we've learned about some of the slow
tools. We didn't look into Manta Corp or Mythix. But these are
also tools that you can use Manta Corp is going to be
another tool from the trilobites team. And Mythix is actually a
smart contract security service from the consensus team, you
basically send a bot that they have running in the cloud your
contract
s and will do some automated process to check for
security vulnerabilities. This is a paid service. But if you're
going to be deploying a protocol that's worth millions of
dollars, spending a few $1,000. Spending a few $1,000 to make
sure it actually does. What it says is going to do correctly is
definitely something that you want to invest in. After you run
through this whole process. You the smart contract developers
and the auditors should discuss their findings. And if there's
any issues, re
peat the steps, repeat all the steps again after
changes are made. So this audit process and making sure your
contracts are secure is a long process. And then afterwards, an
auditor will finally write your report with all security
vulnerabilities and everything that they found in your
contracts. Typically, you'll organize reports in a chart
that'll look something like this, you'll label issues that
have a high chance of happening and have a high impact as
critical things that have a high impact,
but a low likelihood as
medium, and etc. I'm also going to leave some examples two
audits that have been done in the past so that you can take a
look at them. And you can see what a full audit looks like on
certain code. We'll be looking at openzeppelin sigma prime, and
trilobites, because these are three of what I think are some
of the best auditors in the space. Now in the GitHub repo.
We also have a ton of other tools that you can use Mythix,
mithril ethers play and consensus security tools.
If you
want to learn more about security and auditing, I highly
recommend that after this course you play the Ethernet game and
damn vulnerable Defy. These are two games that will teach you a
ton about security. And we'll test the chops and we'll test
everything that you've learned in this course. There's also a
couple of security focused blogs that I really like. One of them
in particular is wrecked dot news. They keep a running list
of some of the largest hacks that have ever happened in the
space and then retrospectives on why those actually happened. And
they usually make it very entertaining as well. We have
some articles in here as well. One of the best places to look
at is this known attacks section where they talk Talk about
reentrancy, Oracle manipulation, front running and a ton of other
attacks that you should absolutely be aware of when
writing your smart contracts, we're not going to go over them
here because they do a great job in these resources explaining
them, you sho
uld also check out this article because I helped
write it. So definitely check that out. And then we've got a
list to even more sections. So this is going to be a living
section here. So please feel free. If you find more things in
the future, please feel free to make pull requests and update
this repository so that other people can learn and know more
about security and auditing and have contract examples on what
bad code looks like and how to actually catch them. Even though
this was one of ou
r quickest sections. From a video
standpoint, this actually is going to be one of the longest
sections of your career. Security is something that is
always going to be on your mind. And there's always going to be
new tools to help with security. And there's always going to be
new things to think about. So even though we went through this
very quickly, I would 100% want you to pause this video, and
work with and try out some of the tools we tried here. And
then maybe even try coming up with your
own vulnerabilities as
well. And with that being said, you have just finished the last
section of this massive master course on learning smart
contracts, solidity, web three and blockchain development, you should be incredibly proud
of yourself. Congratulations, I and the web three community as a
whole want to congratulate you for completing this absolutely
monstrosity of a tutorial, you have done an amazing job to get
this far. And to watch me talking to you right now. And if
you haven't finish
ed the course, go back and finish it before
coming here. We have learned so much on this journey. And I can
say from the bottom of my soul that I am so glad to have you in
the web three space, smart contract space, the blockchain
space, the cryptocurrency space, we are so excited that you're
here. I'm really looking forward to seeing you in the web three
in the blockchain community. Now a lot of people ask, Well, where
do I go? Now, I didn't have all this newfound knowledge. I'm
armed with the i
ntelligence of the web three developer space.
Well, I've left some links in the GitHub repository to lead
you to those next steps. But the biggest thing that you can do
for yourself right now is go take what you've learned, and
apply it somewhere. This is going to be probably the most
thorough course you will ever go through in this space. And you
can go tutorial to tutorial and boot camp to bootcamp all you
want. But at some point, you have to make that leap, and you
have to dive in. And that's
where the majority of the growth
is going to be anyways. So if you're here, wondering where to
go next, go join a hackathon. Go start jumping into issues on
GitHub repos, go start applying for grants, go start applying
for jobs and say, I took Patrick's massive course. Here's
my GitHub repo, work on a personal project, work on
somebody else's project. Take this knowledge and apply it, the
challenges that you'll run into and the challenges that you'll
face. Really trying to do something without
me hand
holding you is where you're going to learn 10 times as much
as what you've learned here. I've walked through as deep down
this rabbit hole as I can take you. Now it's up to you to go
out and do something with it. So thank you, everybody who helped
me create this course. Thank you for taking this course. And I'm
so excited to see you in the community and see what you build
and see what we can create with this technology.
Comments