Afrikaans
Akan
Albanian
Amharic
Arabic
Armenian
Azerbaijani
Basque
Belarusian
Bemba
Bengali
Bihari
Bosnian
Breton
Bulgarian
Cambodian
Catalan
Cebuano
Cherokee
Chichewa
Chinese (Simplified)
Chinese (Traditional)
Corsican
Croatian
Czech
Danish
Dutch
English
Esperanto
Estonian
Ewe
Faroese
Filipino
Finnish
French
Frisian
Ga
Galician
Georgian
German
Greek
Guarani
Gujarati
Haitian Creole
Hausa
Hawaiian
Hebrew
Hindi
Hmong
Hungarian
Icelandic
Igbo
Indonesian
Interlingua
Irish
Italian
Japanese
Javanese
Kannada
Kazakh
Kinyarwanda
Kirundi
Kongo
Korean
Krio (Sierra Leone)
Kurdish
Kurdish (Soranรฎ)
Kyrgyz
Laothian
Latin
Latvian
Lingala
Lithuanian
Lozi
Luganda
Luo
Luxembourgish
Macedonian
Malagasy
Malay
Malayalam
Maltese
Maori
Marathi
Mauritian Creole
Moldavian
Mongolian
Myanmar (Burmese)
Montenegrin
Nepali
Nigerian Pidgin
Northern Sotho
Norwegian
Norwegian (Nynorsk)
Occitan
Oriya
Oromo
Pashto
Polish
Portuguese (Brazil)
Portuguese (Portugal)
Punjabi
Quechua
Romanian
Romansh
Runyakitara
Russian
Samoan
Scots Gaelic
Serbian
Serbo-Croatian
Sesotho
Setswana
Seychellois Creole
Shona
Sindhi
Sinhalese
Slovak
Slovenian
Somali
Spanish
Spanish (Latin American)
Sundanese
Swahili
Swedish
Tajik
Tamil
Tatar
Telugu
Thai
Tigrinya
Tonga
Tshiluba
Tumbuka
Turkish
Turkmen
Twi
Uighur
Ukrainian
Urdu
Uzbek
Vietnamese
Welsh
Wolof
Xhosa
Yiddish
Yoruba
Zulu
all right welcome back welcome to the
defi section of this course we scroll
all the way down here Foundry defi we
can see the code that we're going to
work with for this course now I got to
say ahead of time defi is an absolutely
massive topic quite frankly that
deserves an entire video of itself so
we're just going to give you a brief
rundown of what defi is what you can do
with it and some of the most popular
apps out there so first to get started
we can use this site called defi Lama to
get a snapshot of what's going on in the
defi world right now the total value
locked in defi some of the dominant apps
and what the dominant Protocols are
doing for me we can look at some of
these top five for now too L is a liquid
staking platform which I'm not going to
go into right now makeer da is a CDP or
a collateralized debt position protocol
for making stable coins which is what
we're going to be building a is a
borrowing lending protocol like an open-
Source decentralized Bank curve Finance
is a decentralized exchange more
specifically for working with these
stable coins and then Unis swap is a
general purpose exchange for swapping
tokens and different types of assets to
me if you understand some of these top
ones as you go down the list you can get
more and more intricate or more and more
specific different types of defi
protocols the beauty of Defi and
decentralized finance is that you get
access to sophisticated Financial
products and instruments in a
decentralized context if you're not
super familiar with financial
terminology
some of this might be a little bit
difficult to grasp but it is a
phenomenal Rabbit Hole to go down and it
is a absolute ton of fun there's so many
cool things you can do in Defi and in my
mind defi is one of if not the most
interesting and most important
industries and applications that smart
contracts enable so I highly recommend
you get used to some def some great
places to learn about defi are going to
be places like bankless which has a
podcast that I listen to again like I
said metamask learn to learn more
specifically about wallets and safety
about wallets and honestly so many more
most Concepts that you've learned in web
2 or traditional Finance translate over
to defi really well one of my favorite
things to tell people to do is to try
out a and Unis swap to really see how
some of these protocols work so if we
scroll down here we can go to the
website same thing with this one scroll
down we can go to the website here in a
we can even launch the app and we can
see
what this app even looks like let's go
ahead connect our wallet here I am going
to switch to I'm going to check my chain
that I'm on we're on too testnet if you
look up at the top one thing that's
really cool is you can see this little
ipfs thing here this little ipfs thing
only happens with brave but it means
that this site is also hosted on ipfs
right web 3 all about decentral but if
we scroll down we can see we can supply
different assets and get an interest
rate on those assets we Supply them for
example if we were to supply usdc which
is a United States dollar Peg stable
coin we'd get 2.45% interest similar
exactly to how a bank account works the
reason that we get this interest is
because there are other people who are
borrowing these assets again similar to
a bank and they get charged in interest
rate for borrowing these assets so if
you supply assets you get given an
interest rate and if you borrow assets
you get you have to pay some interest
rate this is why this protocol is so
cool it's permissionless banking
permissionless borrowing and lending of
assets Unis swap is another really cool
application where you can very easily
swap tokens between each other for
example let's if I were to switch to
ethereum mainnet I could choose
something like eth and swap to and asset
the a governance token and it's a
permissionless decentralized exchange
and really enables access to financial
markets in a much more transparent more
accountable and fair way and I just get
absolutely excited about working with
these for those of you looking to get
started here I definitely recommend
trying to use some of these on test Nets
unfortunately not all of these work on
test Nets but I have a couple videos
that shows actually working with some of
these that we can that we can go ahead
and actually watch such as leverage
trading in defi become a defi Quant
flash loans with a which is an advanced
defi tool and a couple of more and when
working with these I don't recommend
doing this on the eum main chain I would
recommend doing something like polygon
optimism or arbitrum where it's going to
be a lot cheaper to make transactions
ethereum fees right now are too high and
that's why we have scaling with layer
twos like arbitrum and optimism one
other thing that I should absolutely
mention now that we're getting into defi
is that there's this topic that we're
not going to have time to go over called
meev M or minor extractable value or
maximal extractable value depending on
who you ask is something something that
plagues the defi industry and something
that a lot of eth cord devs and
protocols like flash Bots are working on
right now basically the concept is if
you are the validator who gets to order
the transactions in a block you can
order those transactions in such a way
that benefit you there are a lot of
different protocols working on
mitigating me and preventing me in the
future or making it more fair and
there's all types of opinions on me I
100% recommend that if you're looking to
go more into really deep meev more into
really deep defi stuff you check out
this flash.net new to me repo and check
out some of these videos and blogs they
are absolutely astonishing to read they
are a ton of fun to read and also show a
terrifying side to the ethereum world as
well but in any case the project that
we're going to be building in this
course is going to be a stable coin now
the concept of stable coins is a bit of
an advanced topic believe it or not
maybe you've heard some stuff about it
maybe haven't in any case we're going to
watch a video that I made previously
about stable coins and what they really
are and what a lot of mainstream media
actually gets wrong about them and by
doing this we as developers are going to
have a much better understanding of what
these stable coins really are and how
they actually work under the hood this
is going to be a tough lesson and I'm
going to say this straight up it's going
to be very difficult there's a lot going
on here I want you to be absolutely sure
to go to chat gbt and ask chat gbt
questions absolutely in the GitHub repo
associated with this course be sure to
use this discussion Tab and ask a lot of
questions in fact if you want you can
even browse around the makerd Forum
which is the protocol behind the die
token which is what our project that
we're going to build is very roughly
based off of and kind of read what
people in the industry are actually
working with and actually doing like I
said there is a lot to defy and this
project that we're making I'm actually
planning on getting audited after we
release this course and this is easily
going to be our most advanced project in
the course hands down even though we
have a couple lessons after this
upgrades governance and introduction to
security this Foundry stable coin
project is the most advanced product
we're working with hands down if you're
able to go through this and understand
everything in this lesson you should be
incredibly proud of yourself and
Incredibly excited to move forward
because this is a hard project and
that's okay take your time don't
overburden yourself there's a lot to go
through here if you've never understood
defi take some time and look into defi
if we go to Google we look up like learn
defi we get a to coin Telegraph article
get some medium
articles maybe even Udi coinbase
coinbase actually has a great article on
this there's a lot of places to learn
about D5 so yes I do recommend if you're
completely unfamiliar with D5
pause the video now take at least 15 20
minutes just to do some Google searching
around right any knowledge that primes
you for this next session is great and
there's really just so much to defi the
main thing is that defi is
permissionless open-source finance and
to me it is the absolute best thing
about smart contracts with defi we can
actually move away from financial
institutions and products that have no
transparency that don't help you that
have centralization risk in the last 20
years have been a history lesson on why
we need defi to take over the world so
definitely want you to do a little bit
of research here so let's do a walk
through the code and then let's walk
through this video on what stable coins
actually
are so here's our codebase if we go into
SRC we see two main files we have
decentralized stable coin. soul and if
we look at into here it's a real
minimalistic very basic is erc20 we've
got some more interesting more advanced
Imports such as erc20 burnable and
ownable but here it looks real similar
to an erc20 right we've got a
Constructor with erc20 we have a burn
function where we can actually burn
tokens remove tokens and we have a mint
function where we can mint and print new
tokens but the main contract is this DSC
engine. soul so this decentralized
stable coin is controlled by this DSC
engine and this has a ton of stuff in
here so of the main functionality in
here is going to be where we deposit
collateral and mint DSC so this stable
coin is going to work because it's going
to be backed by collaterals that have
monetary value we'll talk about more
about that in a minute you can redeem
your collateral remove your collateral
look at all this natat spec by the way
look at all these these comments we we
can burn DSC we can burn our
decentralized stable coin people can get
liquidated what the heck is liquidation
we'll talk about that later they can
mint DSC if they have enough collateral
and we'll talk about collateral soon
deposit collateral redeem collateral Etc
but there's a whole bunch of collateral
stuff and we'll explain that in a little
bit we have this test folder where we
have our unit tests where we go over
engine where we test the engine the
stable coin and also this Oracle lib
thing we have mocks which we know mocks
are and then we have this thing that we
kind of briefly went over which is fuzz
test and we have this invariant thing
that what is this invariant thing and we
have this new type of invariant
underscore test which we're going to go
over and we're going to explain these
invariant tests separate these invariant
tests in my mind separate mediocre
solidity developers from incredibly
powerful incredibly Advanced solidity
developers we've got our scripts as well
real minimal here we mainly just deploy
the stable coin we're going to be using
chain link price feeds to measure the
price of the underlying collateral again
we'll talk about that soon but this is
really the the highle walkthrough and
you can find all the code and all the
information you need on this repo like I
said this is going to be an advanced
section take your time with it be sure
to ask questions use all the tools you
have at your disposal to code along and
understand what we're doing but the
other reason why I wanted to make a
stable coin here is because I strongly
believe stable coin coins are going to
be one of the most important D5
Primitives or D5 products that web 3 has
to offer and in my opinion right now the
current Solutions aren't good enough so
hopefully this inspires some of you to
go out and build a better one but let's
go ahead let's go into this video let's
learn about stable coins and take your
time learning this all right let's watch
this
video when you research stable coins
today you get a lot of misleading
information and that's what we're here
to clarify today in this video we're
going to teach you everything you need
to know about stable coins but actually
what are stable coins why we care
categories and properties designs of top
stable coins and then the real reason
behind what they do what stable coins
really do this video is for both
everybody Technical and non-technical
and we're going to correct a lot of the
misleading information out there there's
going to be a lot of information here
for people less familiar with defo so as
always don't be discouraged if something
doesn't make sense the first time you
buckle up buckle up dum dum we're in for
a volatile ride about stability a stable
coin is a nonvolatile crypto asset
that's it fleshed out we could rephrase
it to a stable coin is a crypto asset
whose buying power fluctuates very
little relative to the rest of the
market and this is the first place where
we disagree with traditional media if
you Google what is a stablecoin you'll
see something like this response
everywhere stable coins are
cryptocurrencies the value which is
pegged or tied to that of of another
currency commodity or financial
instrument and to that I disagree I
think that's an easy initial way to
understand them but not the whole story
a stable coin is a crypto asset whose
buying power stays relatively stable a
good example of buying power would be an
Apple Market if you want to a market to
buy apples with Bitcoin 6 months ago the
number of apples you could buy would be
drastically different than the number of
apples you could buy today that's an
example of buying power changing and not
being very stable however someone buying
apples with do
would probably be able to buy the same
amount of apples 6 months ago to now
that's an example of buying power
staying relatively the same since we
could buy the same amount of apples
today than 6 months ago a dollar would
be considered a more stable asset
whereas Bitcoin would be much less
stable this is what we mean by buying
power an asset whose price fluctuates
rapidly all the time is a poor example
of stable buying power oh it went to
zero this for example would not be a
stable asset now a stable crypto asset
is just a stable asset that's a
cryptocurrency most cryptocurrencies by
Nature aren't stable but we will give
examples of stable coins so summary here
a stable coin is a crypto asset whose
buying power stays relatively the same
and if that's all you take from this
video Perfect great let's keep going now
why do we care about stable coins
because money is
important but not like I Love Money type
of important in everyday Society we need
some type of low volatility AKA stable
currency to fulfill the three functions
of money and for web 3 we need a crypto
version of this the three functions of
money are storage of value unit of
account and medium of exchange storage
of value is a way for us to keep the
value and wealth we've generated putting
dollars in your bank account or buying
stocks cryptocurrencies is a good
example of storing your value apples
would make for a poor storage value
since they would rot over time and lose
their value unit of account is a way to
measure how valuable something is when
you go shopping you see prices being
listed in terms of dollars this is an
example of the dollar being used as a
unit of account pricing something in
Bitcoin would be a poor unit of account
since the prices would change all the
time medium of exchange is an agreed
upon method to transact with each other
buying groceries with dollars is a good
example of using dollars as a medium of
exchange buying groceries with car tires
would make for a poor medium of exchange
since car tires are hard to transact
with in order for our everyday lives to
be efficient we need money to do all
these three things and like I said in
web 3 we need a web 3 money we need to
be able to know the value of stuff in
web 3 be able to pay for things without
prices going crazy and we need to be
able to store our wealth in web 3 in a
decentralized world we need a
decentralized money assets like ethereum
work great as a storage of value and
medium of exchange but fall a little bit
on their unit of account due to their
buying power of volatility however maybe
in the future as ethereum BEC more
adapted it'll become stable and and we
won't even need stable coins but right
now it's a little too volatile and yet
I'll still tell you it's ultrasound
money since I do think it's an amazing
store value but that's a conversation
for another time banket don't kill me
I'd love your content also if you're
here be sure to like And subscribe
because we're just cooking the fire up
and we are about to pop off anyways okay
so now we know what a stable coin is why
we care let's talk about the different
categories of stable coins and here is
the second place I strongly disagree
with traditional media if you search for
types of stable coins you'll get some
that pulls them into categories like
this Fiat collateralized crypto
collateralized commodity collateralized
and then algorithmic now this isn't too
bad of a categorization it does make it
easier for new people to understand but
I think it paints a in acccurate picture
so let's categorize stable coins but
actually the categorizations that I like
are relative stability stability method
and collateral type so what are these
categorizations let's start with
relative stability when we talk about
stability something is stable only
relative to something else the most
popular type of stable coins is pegging
or anchored stable coins and these are
stable coins that are pegged or anchored
to another asset like the US dollar
tether D and usdc are all examples of US
dollar pegged stable coins they Follow
The Narrative of one of these coins
equals $1 and that's how they stay
stable it's stable because they track
the price of another asset that we think
is stable
and most of these stable coins have some
type of mechanism to make them almost
interchangeable with their pegged asset
for example usdc says that for every
usdc token printed or minted there is a
dollar or a bunch of assets that equal a
dollar in some bank account somewhere so
the way it keeps its value is that at
any time you should be able to swap your
usdc for the dollar or at least
hypothetically so something like d on
the other hand uses a permissionless
over collateral ization to maintain its
Peg but we'll get to understanding that
a little later however a stable coin
doesn't have to be pegged to another
asset it can be floating remember to be
considered a stable coin its buying
power just has to stay relatively the
same over time so a floating stable coin
is floating because its buying power
stays the same and it's not tied down to
any other asset with this mechanism you
could hypothetically have a stable coin
that's even more stable than an anchored
or pegged stable coin let's look at an
example let's say I can buy 10 apples
for $10 today but in five years I can
only buy five apples with $10 so it
would cost me $20 to buy 10 apples this
isn't an unheard of phenomenon to happen
and it's commonly just referred to as
inflation now let's introduce a stable
coin whose buying power floats up and
down with the market for now let's just
pretend it does so magically and let's
call it Apple coin today you can buy 10
apples with 10 Apple coins and in 10
years you can also buy 10 apples with 10
Apple coins which one of these assets
would you say is more stable yes the
Apple coin's buying power stayed the
same over the 5-year period so we'd say
it's a more stable asset even though
it's not pegged to something like a
dollar and Yes stable coins like this do
exist and they use a lot of clever
algorithms to do this one such example
is the Ry stable coin by reflexor Labs
how Ry Works might be its own video but
we've left some links in the description
for you if you want to learn more about
how it works because this concept can be
a little hard to grasp to further
explain it let's go through another
analogy let's look at the anchor and the
buoy in this image which of these do you
think is more stable the anchor or the
buoy well it depends on what you're
comparing them to compared to sea level
the buoy is more stable since it'll
always be at sea level as the water
level rises and Falls the distance
between the anchor and the sea level is
constantly changing the buoy is an
example of a floating stable coin now if
we compare these two to the ocean floor
though the anchor is more stable of the
two since it'll always be right on the
ocean floor this is analogous to a
pegged SL anchored stable coin now if
we're going to be really serious with
this analogy then the question might be
well what happens when a storm comes or
what happens when the water's crazy or
the Tide's crazy or something and to
that I'd say you're right and a stable
coin needs to take extra precautions to
take these into account and maybe get
the average sea level over the course of
time and most popular floating stable
coins have some mechanism to account for
turbulence like that summary Peg stable
coins are tied to that of another asset
while floating stable coins use
different mechanisms to keep the same
buying power over time number two
stability method the stability method is
this mechanism that keeps the coin
stable if it's a peg stable coin what is
the pegging mechanism if it's a floating
stable coin well what is the floating
mechanism and it typically revolves
around minting and burning the stable
coins in very specific ways and usually
refers to who or what is doing the
minting and burning these are on a
spectrum of governed to algorithmic in a
governed stable coin there is a
governing body or a centralized body
that is minting and burning the stable
coins you can imagine the maximumly
governed and least algorithmic coin
would be a single person/ entity minting
new stable coins promising that the
coins are not volatile but it could also
be an organization like a government or
even a dow choosing to Mint and burn new
coins these govern coins are typically
considered centralized since there is a
singular body that is controlling the
minting and burning you can make them
more decentralized by introducing a dow
and that kind of makes them more
algorithmic but we'll get more into that
later coins like usdc usdt and TUSD are
examples of governed stable coins on the
other hand an algorithmic stable coin is
a stable coin whose stability is
maintained by a permissionless algorithm
with no human intervention and this is
the third place where I disagree with
traditional media a coin like dy I would
consider much more algorithmic than
govern because uses a permissionless
algorithm to Mint and burn tokens
whereas traditional media might say an
algorithmic stable coin is always under
collateralized or follow senior in
shares or something like that but an
algorithmic stable coin is just when a
set of autonomous code or algorithm
dictates the minting and burning there
are zero meddling humans would have been
mine if it hadn't been to those meddling
kids examples of algorithmic stable
coins are going to be die frax Ry and
yes the $40 billion
disaster us yes we're going to talk a
little bit more about classic us and
Luna now a token can have algorithmic
and governed properties in the same way
that it can be somewhere in the middle
of being floating and pegged D for
example does have an autonomous set of
code that dictates the minting and
burning but it does also have a dow
where they can vote on different
interest rates and what can be
collateral types and different things
like that so technically it is a hybrid
system it has some governance mechanisms
and also some algorithmic ones usdc C
would fall purely in the governed
category because it's controlled by a
centralized body us and Luna would fall
almost purely in algorithmic the dirt
roads Blog has some amazing takes on
these pieces and a wonderful
visualization of where on a spectrum of
coins that are more algorithmic or
govern they use dumb as the opposite of
algorithmic instead of govern which
probably isn't wrong most classically
categorized Fiat collateralized stable
coin almost all fall into the Govern or
dumb section since they are dealing with
fiat currency and you need a centralized
entity to onboard that Fiat to the
blockchain you'll also notice on this
chart they have anchored versus
reflexive on the x-axis that's referring
to how the collateral type affects the
stable coin and collateral type is what
we're going to cover next so the summary
here though is algorithmic stable coins
use some sort of autonomous
permissionless code to Mint and burn
tokens whereas a govern stable coin have
some human interaction that mints and
burns the coins and keeps them stable
now before we go into to our final
category let's look at this chart again
we could replace the word anchored with
exogenous and reflexive with endogenous
and we'd have a chart that shows
collateral type versus stability
mechanism which brings us to number
three collateral type now when we say
collateral we mean the stuff backing our
stable coins and giving it value for
example usdc has the dollar as its
collateral and it's the dollar that
gives the usdc token its value because
you hypothetically can swap one usdc for
$1 die is collateralized by many assets
for example you could deposit eth and
get minted dye in return and US was in a
roundabout way collateralized by Luna
exogenous collateral is collateral that
originates from outside the protocol and
endogenous collateral originates from
inside the protocol so one of the easier
ways to Define what type of collateral
protocol is using is to ask this
question if the stable coin fails does
the underlying collateral also fail if
yes it's endogenous if no it's exogenous
if usdc fails the protocol does the
underlying collateral the dollar failed
no so the protocol has exogenous
collateral if the usdc stable coin fails
the dollar is going to keep being the
dollar if die the stable coin fails does
the underlying collateral eth also fail
no so the die system is exogenous the
value of eth isn't dependent on the
value of Dy if us fails does the
underlying collateral Luna Tera fail yes
absolutely and this is exactly what
happened that caused the system to lose
40 billion dollars in what seemed like a
day exogenous collateral originates from
outside the protocol endogenous
collateral originates from inside the
protocol two other good tests that you
can ask are was the collateral created
with the sole purpose of being
collateral or does the protocol own the
issuance of the underlying collateral if
the answer is yes to either one of those
then it's endogenous collateral now the
traditional media usually says that
algorithmic stable coins are to blame
but I think what they're really
referring to is endogenously
collateralized table coins it makes
sense that they can be scary and
potentially dangerous because their
value kind of comes from nothing
exogenously collateralized stable coins
are typically over collateralized
meaning there's more value of collateral
than there is of the stable coins here
we have another image from dirt roads
comparing different stable coins the
exogenous versus endogenous collateral
of the protocol calls and how much they
have maker d/ diey has almost all
exogenous collateral frax which is
another stable coin we haven't really
spoken about too much has a mix of
exogenous and endogenous collateral and
the old teral Luna and US system had
mainly endogenous collateral which is
how the system was able to crumble so
quickly so yeah endogenously
collateralized stable coins don't have a
great track record so why would you want
to make one well the answer is scale and
often times people also say Capital
efficiency with exogenously
collateralized stable coins the only way
you can mint more stable coins is by
onboarding more collateral you can only
have a stable coin market cap that is
high or as high as that is the value of
all your collateral so if you want to
have $68 billion in stable coins that
that means you need to have $68 billion
worth of collateral and that's a lot of
money that you would need to onboard to
your system if you have an endogenously
collateralized stable coin you can have
$0 worth of collateral meaning it's much
easier to become massive faster now I
agree with the dirt roads publication
when they say that exha usly
collateralized stable coins can't scale
and I talk more about that in the blog
associated with this video so if you're
interested be sure to check that out
after the rest of this video but watch
the rest of this video cuz we're just
getting started in the blog we also talk
more about seniorage shares and shelling
coin logic which if you're interested in
that stuff definitely check it out most
of these endogenous coins can be traced
back to a paper written by a man named
Robert Sam
where he talks about how to build an
endogenously collateralized stable coin
using a seniorage shares model which
again I'm not going to go into but I
wanted to mention it because it's
probably one of the most influential
papers when it comes to these
endogenously collateralized stable coins
endogenously collateralized stable coins
there a lot of words now there's more
information on the endogenous collateral
debate in the blog but let's do a
thought experiment that I do think is
compelling for endogenous collateralized
stable coins endogenously collateralized
stable coins imagine you have a currency
and it's collateralized 100% by gold and
you run a bank and it's open 24/7 to
allow people to exchange your generic
coin for gold in your vaults people love
the convenience of our stable coin
instead of having to Lug around their
gold so they treat our stable coin as if
it was gold because they know that at
any time they can exchange it at the
bank this is an example of our generic
coin being exogenously collateralized by
gold pegged to its price and governed by
By Us by Me by our entity since we own
the issuance and burning of the coin you
come to our bank we'll issue this coin
or we'll burn it once you redeem your
gold and since you can always exchange
our coin for gold our coin keeps its
value now let's say the bank is only
open 5 days a week does our coin lose
its value now that you can't always
exchange it for gold well probably not
the market probably won't even care now
let's say you need to close the bank for
a week for renovations does our coin
lose its value now well let's say we
close the bank for a month or a year or
a decade forever if we get to the point
where you could never actually exchange
our coin for gold or the underlying
collateral again maybe do people just
use the coin as its currency backed by
nothing in a way now it's moved from
being exogenous to now being endogenous
since it's now backed by well itself
this is why dirt roads has reflexive on
their chart one of our coins is no
longer equal to some amount of gold it
now equals one of
itself okay I know there's a lot here
and there's even more in the blog so if
some of this confused you don't be
discouraged you can always come back
there's a ton of supporting documents
and blogs and links that I personally
watch to learn a lot of this as well so
be sure to use that if you get confused
but now let's look at some of the top
stable coins what their properties are
and then we're going to get to some
really interesting stuff we're not going
to go too deep into the architectures of
the stable coins we are going to go over
d a little bit more though because the
die/ maker di system is a pretty
standard model for what a stable coin
could look like and it was one of the
most influential stable coins ever
created so there's that and it's
important that we go through these so we
can understand what's currently out
there and why they're currently out
there and then we can
reveal why they're really out there why
there's so many stable coins what are
the incentives for people to Mint them
and it may just blow your mind so let's
start with Dy as we've mentioned before
Dy is a pegged algorithmic and
exogenously collateralized stable coin
it's one of the most influential di
projects ever created and was a huge
factor in supercharging the dii space
roughly the way it works is you deposit
eth or some other crypto collateral into
the smart contract that has this die
algorithm code and based off the current
collateral to US dollar or eth to US
dollar price it'll me you some amount of
die you can only mint less dye than the
total value of collateral or eat that
you have this way the system always has
more collateral then they'd have minted
dye additionally every year or so you'll
get charged something called a stability
fee usually around like 2% and now
you're free to do whatever you want with
your die if you want to get your eth
back you have to give your die back to
the smart contract which will then burn
your diey it'll use the current price of
e to figure out how much money it should
give back to you it's because of this
stability fee and collateralized E that
people often refer to this system as a
collateralized debt position since we
technically owe D back to the protocol
at some point so yes all the Dy that's
in existence somebody minted from the
maker protocol and needs to pay it back
at some point if you can't pay your
stability fees or the price of e tanks
and now the value of our collateral is
less than the value of the die that we
minted people can liquidate us which
means they can take our collateral
protocol always needs to have more
collateral than men to die so this is
sort of your punishment for not keeping
the collateral up and a way to save the
system from becoming under
collateralized and then there's also a
maker token that's used to vote for
stuff now the reason I give this
overview is I want your brain to be
asking the question hey uh I get charged
to Mint a stable coin all the die in the
world somebody's being charged to have
it out there someone could take my
collateral if I don't monitor the
balance and most importantly why would I
spend money to Mint this stable coin why
would I be the one to do that great
question that is the fundamental
question and we'll get to that usdc usdc
is a classic pegged governed exogenously
collateralized stable coin it's backed
by real world dollars in a bank account
not much else to say here USD and Luna
the classic old USD and Luna we know it
collapsed but we can learn from what
happened to hopefully prevent it in the
future us was a stable coin pegged to
the dollar algorithmic and endogenous
and it imploded on itself using what we
know about stable coins can you guess
why well since the system was endogenous
once Us lost its Peg Luna became less
attractive to hold since people weren't
holding Luna the price fell and it made
it harder to keep the peg of us which
made Luna's price fall which makes us
harder to hold its bag which makes you
see the pattern people still want to try
using these endogenously collateralized
stable coins because they do scale so
quickly so protocols like frax have come
a long way to do some type of hybrid
between endogenous and exogenously
collateralized stable coin Rye Ry is one
of the few floating stable coins where
it's not pegged to any other asset it's
focused on minimal governance being
floating and using only ethereum as
their type of collateral with a nearly
purely algorithmic stability mechanism
in a way one could argue that because
their collateral is only e the price of
Ry will always be somewhat pegged to the
price of eth but that might be a longer
argument due to the it's floating
algorithmic and exogenously
collateralized now they use some really
cool supply and demand mechanisms to
keep the price stable and nonvolatile
but it's not really important how it
works for the purpose of this video so
once again I'm going to leave some links
in the description if you want to learn
more about Ry the video on screen right
now I think is particularly good at
explaining exactly how it works and
explaining why it's such a good stable
coin for normal average people to have
so now that we've gone over some stable
coins we've gone over the categories why
why we care what is a stable coin let's
talk about what they really do we can
start by asking the question okay which
one of these is the best stable coin and
to that I need to ask the best stable
coin for who centralized govern coins
obviously have the issue of centrality
which sort of defeats the purpose of
being in web 3 so maybe we want some
flavors of algorithmic stable coins
maybe that's probably what we want for
web 3 but these algorithmic coins might
feel untested to non- crypto people and
the fees ass associated with them might
be a little bit scary for me personally
like I said I really love the idea of Ry
the idea is to have stable buying power
as opposed to being pegged to some other
asset and it's algorithmic nature as
opposed to being centralized so it's a
decentralized stable coin that's what we
want but every coin has their trade-offs
and I'd argue there is definitely no
best coin right now the stable coin
that's best for the average person might
matter much less it's the stable coin
that's best for Rich whales might be
what's more important here yes the
stable coins the whales like might be
what's more important now for most
algorithmic stable coins you'll see this
some sort of fee associated with minting
the coins protocols do make money off of
the stable coin systems which I think is
good sometimes they need money for
maintenance incentives for the stability
of the coin or money for improvements so
I do think these fees are good and we
need stable coins for the three
functions of money storage of value unit
of account and medium of exchange but
are you going to be the one to pay these
fees to Mint them and keep them in
circulation someone has to pay to Mint
these coins and often keep paying the
market cap for some of these stable
coins is in the billions if there is a
1% fee on these and the market cap is 1
billion we're talking about $10 million
our average people going to collectively
pay $10 million a year to keep these in
circulation no so average people aren't
printing these for the three functions
of money well then who is minting these
so let's play a little thought
experiment let's say I have eth as an
investment and I've bought up all the e
I've sold my house I've sold everything
I own and I've used everything I have to
buy ethereum but I want more what can I
do I can put my eth into one of these
stable coin protocols get the minted
stable coin and then sell the stable
coin for more eth you might have heard
Concepts like Leverage investing or
margin trading and this is essentially
the web 3 equivalent it's kind of funny
why are stable coins good well because
we need those three functions of money
but why are stable coins minted well
because investors want to make leveraged
bets in fact most stable coin platforms
have this as one of the biggest reasons
to minth their stable coin to multiply
exposure or to maximize your position on
some crypto asset now for sophisticated
investors this isn't new information at
all this is like investing 101 however
it does feel weird that we need stable
coins for the three functions money but
that's not why they're minted so even
though I said I really like Ry as a
stable coin for the people a reason D
might be higher market cap is that
investors like The Leverage opportunity
much more than they like the leverage
opportunity that Ry offers it could be
something else but that might be a big
reason how much stable coins are minted
are based off of how much investors
think they can use that stable coin to
get more exposure to assets that they
really want so I know we've gone over a
lot here and the rabbit hole really
doesn't end there curve. Finance being
one of the most important protocols for
stable coins is a really interesting
story as well my friend Garrett who
teaches about technology and finance has
a fantastic example as to why a stable
coin exchange might be so important you
might wonder how a stable coin exchange
ever got off the ground is there really
any demand out there to trade $1 for $1
then I think back to my university days
one weekend my laundry pile grew so
disgusting I was desperate to use the
washing machine immediately but the
laundry machine took quarters and I only
had dollar bills quarters were in such
short supply around the dorm room that I
was willing to pay more than $1 for four
quarters in this urgent moment I had
specific utility in mind and that
changed my personal equation for this
and a and curve both looking to launch
their own stable coins soon is going to
be really interesting as well so we'll
just have to see how these unfold I do
think though that we are going to get
better and better at creating these
stable coins because we do need they are
important and for you developers
watching who want to build one of these
we have some minimal stable coin
contract examples in our def minimal
repo Link in the description so if
you're looking to tackle this problem
definitely be sure to get started there
I'm really excited for the future of
Defi and for the future of stable coins
as I think they are a wildly important
primitive for Defi and I know that this
was a long video but I hope you all
learned something and I hope this gives
you a better idea of what stable coins
really are and how they're created if
you learned something leave a comment in
the comment section if you didn't learn
something leave a comment in the comment
section or if I got something wrong
leave a comment there thank you all for
watching and I'll see you next
time all right we have now learned a ton
about Defi and hopefully I piqu your
Curiosity on how much more there is
still to learn but now we're going to
move on to creating our own stable coin
here so again you can follow along with
all the code that we're going we're
going to be going over in this Foundry
defi stablecoin f23 section of this
course and like I said I'm planning on
getting this code actually audited so be
sure if you have your GitHub repo be
sure to watch this repo and look for
updates I will be posting the audit
reports in this as well I've got a
couple videos on what smart contract
audits are and why they're so important
and I'll leave a couple links in the get
AO associated with this course for those
of you looking to go down the security
track definitely be sure to watch this
video but our right we are finally in
our GitHub repo let's build this stable
coin mkdir foundary defi stablecoin
f23 let's open this up with code or you
know the drill file open folder we're
going to be going a little faster this
lesson because a lot of what we're doing
is just going to be drilling in
information that you already know we
have a couple of new things to go over
such as stateless fuzzing which we'll
talk about in a bit but a lot of the
coding aspect of this you already know
all right we're in our folder Forge nit
clear let's make a little read me over
here and talk about the design of our
protocol so we're going to make a stable
coin but if you watch that stable coin
video we're going to make a stable coin
that is one anchored so when we're
talking about the relative stability
it's going be anchored or pegged to the
US dollar so this means we're going to
have to put some code in here to make
sure that our stable coin is always
worth $1 number two the stability
stability mechanism or the way we do
minting is going to be
algorithmic this means that this is
going to be a decentralized stable coin
there's not going to be any centralized
entity that's going to Mint or burn or
maintain the price this is going to be
100% onchain And algorithmic which is
ideally what we have for the future of
stable coins there's no controlling
entity that controls our stable coins so
this is great A Better stable coin for
web 3 probably isn't anchored or pegged
it's probably floating but that's a much
harder mechanism to do so we're going to
go ahead with the anchor to Peg for the
moment and then finally the collateral
type is going to be you guessed it
exogenous and it's going to be crypto
collateral we're going to use
cryptocurrencies as collateral for for
this currency we're going to use
ethereum and Bitcoin as the collateral
for our system here so with this being
our architecture we're going to keep
this in mind okay how can we actually
make sure that this is always Peg to a
dollar and one way we can do this is
with a chain link price feed so with a
chain link price feed we get the price
feed and we set a function to exchange
eth and Bitcoin or whatever their dollar
equivalent is and this way if right in
our contract we have this exchange set
up price of this stable coin should
hypothetically always be around a dollar
to make the stability mechanism
algorithmic we're going to say people
can only mint stable coin with enough
collateral and that's going to be CED
directly into our protocol and the
collateral type being exogenous of
course we're only going to allow these
two types of cryptocurrencies to be
deposited specifically we're going to
use wrapped eth and wrapped Bitcoin so
the erc20 version of eth and the erc20
version of Bitcoin some might argue that
this wrapped Bitcoin is a little bit
centralized depending on who is
onboarding the Bitcoin into ethereum but
that's another conversation so over
collateralized stablecoin with we and
Bitcoin as the collat for this let's do
this so the first thing we're going to
do is is we're going to well we're going
to get rid of all these goodbye goodbye
goodbye delete and now we're going to
create our two main contracts new file
D
centralized table coin do a capital c.
soul and this is just going to be the
actual token so I'm actually going to
copy paste my layout of functions
because I like them I like to have this
and at the top of course spdx
license identifier MIT if you want you
can just copy this directly from GitHub
repo associated with this course
SRC right here copy this layout here I
like to have it as a reference then you
already know pragma solidity
0.818 and we'll do contract
decentralized
stable coin boom like this and now this
is going to be one of the main
differences between this code writing
here and all the other products that
we've done we're going to be very
verbose with our code documentation and
the reason for this is is when it comes
to Security Professionals who are going
to review this code we're going to make
their lives so much easier if we have a
ton of text explaining what our code is
doing additionally if you work with AIS
AIS are fantastic at reading and
understanding language so the more
language that we have to explain what
our code should do the better off our
code can be sent through some AI model
to make sure that it doesn't have issues
so we're going to go ahead add a little
bit of npec here right from the get-go
at Title
dentra by stable coin all one word at
author Patrick Collins or put your name
here Yep this is open source MIT license
you can do whatever the heck you want
with it I'm even going to say collateral
exogenous eth and BTC
minting or the stability mechanism is
going to be
aloric meaning it's decentralized
relative stability is going to be pegged
to USD then I'm going to say under here
this is the contract meant to be
governed by
DSC engine this contract is just the I'm
going to toggle word rep here ear T20
implementation of our stable coin system
so that's what this is going to be this
decentralized stable coin is purely
going to be an erc20 with minting and
burning and stuff right it's not going
to be it's not going to have any of the
logic the logic is going to be in a
separate contract so let's go ahead and
let's make this so first Constructor
boom and we're going to use open
Zeppelin to get this kick started so
right at the top import well actually
before we even do that Forge install
open Zeppelin slopen Zeppelin D
contracts D- no- commit and we go ahead
we went ahead and installed that now
we're going to open up our Foundry dotl
we're going to add some remappings in
here so REM mappings equals at open
Zeppelin SLC contracts equals lib slopen
Zeppelin slash contracts slash contracts
that looks pretty good right up at at
the top of this now import little named
import
erc20 we're going to import this erc20
burnable contract I'll explain this in a
second and then also erc20 from atop
Zeppelin contracts toen
erc20
extensions erc20 burnable dossou did I
spell this right looks like that that's
right so what did I oh oops looks like I
spelled contracts wrong let's spell it
right okay cool that looks good now what
we're going to say is that our
decentralized stable coin let me even
zoom out a little bit Yep this is going
to be a big one so we're going to zoom
out a little bit here hopefully you all
can see this is ec20 burnable okay and
the reason that we're going to use this
contract if we command clicking it or
just look it up is it has this burn
function and we want this burn function
because this is going to help us
maintain the peg price we're going to be
burning a lot of these tokens you'll
understand that in a bit now same as an
erc20 so the erc20 burnable contract is
an erc20 which is why we can import the
erc20 contract from it as well and the
erc20 burnable Constructor is an erc20
which means we have to use the erc20
Constructor as well which takes a name
where we're going to call ours
decentralized stable coin and DSC like
this and that's it this is going to be
our whole Constructor we're not going to
touch it that's all we're going to do
let me zoom in a little bit I'm going to
keep zooming in and out hopefully it's
not too crazy now we want this coin to
be 100% governed by our engine and our
engine is going to have all this stuff
about what collaterals to use how to use
it what to Peg it to Etc this is purely
just going to be the token so since we
want this token to be 100% controlled by
our logic we're going to make this
ownable as well which means we're going
to have only owner modifiers where the
owner is going to be that immutable
logic we're going to create so open
Zeppelin has a package for that too so
we're going to do import ownable from
atop Zeppelin contracts
access ownable doso I'm going to copy
this so our contract is going to be
erc20 burnable and it's going to be
ownable and there's going to be two
major functions that we want our engine
to own those functions are going to be
function burn where it takes in a un
256 uncore amount public we're going to
override the burn function in burnable
this is going to be only owner so that
only the engine only the logic that we
give it can mint and burn and we're
going to say U 256 balance equals
balance of message. sender and we're
going to make sure that somebody when
somebody tries to burn some token they
at least have that much token so we're
first all going to say if the amount is
less than or equal to zero then they
can't burn right we don't want people to
try burning zero that's silly so we're
going to revert with an error put all of
our errors right up at the top here
error de centralized stable coin
underscore underscore must be more than
zero like this so we're going to revert
with this error and then we're also
going to say if the user's balance is
less than the amount that they're trying
to burn then we're going to revert with
another custom error I'm just going to
copy paste and I'm just going to say
burn amount exceeds balance boom paste
that in like this and then finally we're
going to do this thing called super. bur
which we haven't talked about yet so
this super keyword basically says hey
use the burn function from the parent
class which in this case is the erc20
burnable so all this code is going to
run it's going to hit this line it's
going to say hey go to the Super class
and use the burn function there so our
codee's going to go oh okay well ec20
burnable that's the super class or the
parent class ah just use the burn
function in here which calls the burn
function here in the ec20 so which does
all this stuff in here so that's what
the super keyword does only owner only
engine's going to add this now we're
going to do this function mint this is
going to be an address 2 and a uent
256 amount this is going to be public
excuse me this is actually going to be
external also only owner this one
probably could be external as well but
that'll come out in the audit external
only owner and this is actually going to
return a Boolean when you do mint you
want to have a return a Boolean here
when you do a mint we're going to return
true if it actually works but we're
going to say if 2 equals equals address
zero we're going to do a little
sanitization of the inputs here then
we're going to revert revert with a new
error error to centralized stable coin
not zero address we're not going to let
people accidentally mint to the zero
address because that happens kind of a
lot and we're going to say if the amount
is less than or equal to zero then we're
also going to revert with this more than
zero error here boom and then finally
we're going to return true oh and then
obviously we should run
mintor 2 Comm amount so we're not
overriding any functions in here right
we're just calling the mint function
over here we had to do super because
we're overriding the burn function and
we're saying hey do all this stuff and
then do the regular burn there is no
mint function there's an underscore mint
function that we're going to be calling
and guess what that's it this contract's
done we're not doing any more here Forge
build cool and then we probably want to
write some tests for this write a deploy
script but we're going to do all that in
a little
bit now what we're going to do is we're
going to build the engine the engine to
the car the main components of this
contract you want to take a break and be
proud of you yourself right here go for
it if you want to even pause the video
start writing some of your own tests
write your own deploy scripts go for it
but let's go ahead and start building
this engine we're going to be building
this a little bit different than some of
the other projects we might even be
testing some of this along the way to
make sure we get things right so I'm
going to go ahead create a new file d c
n g. Soul centralize stable coin engine
and let's build this in engine to this
car all right let's go back to let me
grab I'm just going to copy paste this
beginning part let's come to the engine
paste that in we have spdx layout of
contracts
prag contract
DSC
engine this let's give this a lot of
natat speec all right title DSC engine I
don't want that at author a
or metrick Collins the system is
designed to be as minimal as possible
and have the tokens maintain a
$1 maintain a one token equals equals $1
Peg toggle the word WP this stable coin
has the properties Jus collateral dollar
pegged Alor
algorithm stable it is similar to D if D
had no governance no fees and was only
backed by wrapped eth and wrapped
Bitcoin at notice this contract is the
core of the
DSC system it handles all the logic for
minting and redeeming DC as well as
depositing and drawing collateral at
notice this contract is very Loosely
based on die on the maker Dow DSS die
system you might be asking Patrick
that's a lot of text here yes we want a
lot of text when people read our code
our code should be readable remember
your code is going to be written once
read hundreds of thousands of times I
have read the A and maker code so many
times and so many other people have as
well you want your code to be very
verbose so that other people can come
and understand what's going on so let's
begin so let's think about what are the
main functions that our project should
have what are the main things that we
should do right before we start even
start coding anything and often times a
lot of people will take this step and
will actually create an interface for
their code they'll create an interface
and say hey here's all the functions
that I want this to do and then they'll
say hey our contract is that interface
so that they don't forget any of those
functions for us I'm just going to go
ahead and write them out here so I want
one function to be function deposit
collateral and mint DSC I want people to
be able to deposit their die or their
Bitcoin and mint are DSC token I want
people to redeem their collateral or DSC
right when people are done with doing
whatever they want with the stable coin
they can turn the stable coin the the
DSC decentralized stable coin back in
for whatever collateral they originally
used I want people to be able to
function burn their DSC and the reason
for this is if they're nervous that they
have too much stable coin and not enough
collateral and they want just a quick
way to have more collateral than DSC
they can quickly burn stuff which is
another part of the system we should
even put a point in here our DSC should
our DSC system should always be over
collateralized at no point should the
value of all collateral be less than or
equal to the value of all the DSC
or the dollar backed value of all the
DSC we should always have more
collateral than DSE in the system at all
times and we need to code in such a way
so burn DSC is a function that will make
more sense in a little bit we should
have a function called liquidate and
this is going to be a really important
function the reason that we're always
going to have more collateral if the
value of their collateral drops too much
let's say let's say I put in $100 worth
of eth and I minted $50 worth of DSC I
have more collateral than DSC that's
good what if the eth price tanks to $40
40 eth now we are under collateralized
right now we have less eth than we have
DSC and keep and this user should get
what's called liquidated they shouldn't
be allowed to hold a position in our
system system anymore so ideally we set
some threshold that's too low maybe it's
maybe it's 20% and if you hold $60 worth
of eth and $50 worth of D DSC you should
get kicked out of the system because
you're way too close to being under
collateralized this liquidate function
is going to be the function that other
users can call to remove people's
positions to save the protocol and we'll
talk about that a little bit more very
soon and we're going to want a function
Health Factor this should be an external
view function or excuse me get health
factor and this will allow to see how
healthy people are so let's go back up
to this example here so if if the price
of eth dumps to $40 we're now $10 under
collateralized right and that's not good
that's really bad we never want this to
happen so what we can do is we can set a
threshold to let's say for this example
50% or
150% so if you have $50 in the system
you need to have at least 75 eth at all
times this way there's a little bit of a
buffer that way we can never be under
collateralized if the price tanks here
if you go to
$74 now what we can do is we can
liquidate and we can say hey if someone
liquid if someone pays back your borrow
your minted
DSC they can have all your collateral or
a discount so maybe we say hey somebody
pay back this 50 DSC and you can have
this $74 worth of eth somebody's going
to be very incentivized to do this
because they're going to make 20
$24 so we'll set some threshold maybe
150% and we'll we'll say hey anybody who
liquidates your position if you're under
the threshold they can have as a reward
some of your extra collateral this will
incentivize people to always have extra
collateral otherwise they're going to
lose way more money than they borrowed
that didn't make sense so one more time
let's do that example so if I mint so if
I put down $100 worth of eth as
collateral and I mint $50 worth of DSC
now I'm going to go off and do whatever
I want with DSC price of my eth tanks to
$75 or better yet let's say $74 so other
user is going to going to see oh my God
under collateralized and we're going to
let people liquidate their positions if
they become under collateralized based
off the threshold some other users going
to see that and they're going to say
okay I'll pay back the $50 of DSC I'll
pay back the $50 of DC so now this
person has zero debt and in return get
all your collateral so now this person
has $0 worth of eth and this user got
the
$74 and all they to do was pay $50 of
DSC to get $74 of eth so this person is
now up just made $25 or
$24 by liquidating you they're
incentivized to make money and this is
your punishment for letting your
collateral get too low so hopefully that
makes sense if this system of
liquidations doesn't make sense to you
you know where to go come to the GitHub
repos with this course and start joining
the discussion all right so hopefully
this made sense if it doesn't use the
discussions tab of course now these are
kind of this combination function we're
probably going to want a function just
called deposit this deposit collateral
external we're probably going to want a
function
redeem collateral external and then
we're probably going to want along with
a burn DSC we're probably going to want
a function mint DSC external and these
look like these are probably going to be
the majority of what a protocol does and
what a lot of people even do is
sometimes they'll even write tests right
now describing what each one of these
should actually do to the system right
we're not going to do that but we may
actually write some tests as we go along
here and I like to write my deploy
script kind of early and you'll see me
do that here that way I can write tests
using my deploy script so let's go ahead
though where is the best place to start
tackling this well to me the the easiest
place to start is actually with the
depositing right because that's the
first thing people are realistically
going to do with this protocol is
actually deposit the collateral so I'm
going to start there so for this deposit
collateral function what are they going
to want to do here well we're going to
need to let them pick what collateral
they want to deposit so we'll say
address token collateral address and
then also the unit 256 amount collateral
we're going to do a little toggle word
WP all right cool so deposit coll the
token collateral address and then the
amount that they're going to want to do
so already we can see that there's going
to be a whole bunch of stuff that we're
going to want to do here so let's even
doce a little bit of natspec bit of
natspec here we'll say at pram we'll
just explain what the prams are and this
is where get up co-pilot is really
helpful because often times it's really
good with docs so we're going to say Pam
RM so both of those look good token clat
all address the amount of token to
deposit as collateral amount collateral
the amount of collateral to deposit
right real simple so we're going to want
a couple of things here we're going to
want to sanitize this a little bit so
the amount collateral we're definitely
going to want this to be more than zero
so we're probably going to want a
modifier called more than zero that we
can use throughout these functions
sometimes people might accidentally send
a zero transaction we want to
automatically revert those so we scroll
up to the top here we see our modifiers
come before our
functions so we're going to create our
modifiers here and we're even going to
add like a little little section A whole
bunch of these
here boom
modifiers like this cool and we'll say
modifier more than zero and this
modifier will take a un
256 amount and we'll just say if mount
mount equals equals z then we're going
to go ahead and revert with a new error
where do errors go let's scroll to the
top errors
go right underneath Imports actually
it's not quite right they're going to go
right underneath the contract so we're
going to do we're going to copy this
whole
section say errors we're going to go
here we're going to say error DSC engine
uncore uncore what do we want to call
that just needs more than zero needs
more than zero this and we're going to
revert with needs more than zero and of
course add the little underscore here
cool so now we have a more than zero
modifier so we can make this external
more than zero and we'll pass the amount
collateral and boom okay cool we're
doing a little sanitization here what
else should we do you know let's even
copy this we'll paste this here we'll
say
functions and we're going to have a
Constructor
so I'm just going to put this here for
now Constructor should spell Constructor
right and then we're going to have a
section after functions like a
subsection we're going to call it
external
functions this external functions cool
CU we want to go receive and fallback
we're not going to have those but then
external and public so external function
first anyways okay more than zero got
that and we don't want people to use any
collateral right we probably only want
them to use certain collateral that we
allow so we're going to have to create a
new modifier called is allowed token so
we're going to do modifier is allowed
token this is going to take a address
token basically we're going to say if
the token not allowed the token isn't
allowed then revert right however at the
moment we don't have like a token allow
list so let's create that this is
probably going to be a state mapping so
let's scroll to the top where do state
variables go so errors type Declaration
state variables okay errors great let's
put it here State
variables okay and let's do our let's
create an allowed list of mapping so
we'll do
mapping address to BU you know private
sore token to allow and we could do this
however I already know that we're going
to need price feeds so instead what I'm
going to do is I'm not going to do an
address to Bull I'm going to do an
address to address and this is going to
be our price feed mapping so this is
going to be sore price feeds and
normally I do the syntax token to price
feed right well for this we're just
going to call this price feeds and we're
going to use the newer solidity named
mappings to make this a little bit
clearer so I'm going to say address
token maps to address price feed now
this is S price feed and anybody can
look up and go ah okay so this is token
to price feed cool so we're going to
have this list of price feeds and where
should we probably set this well we're
probably going to want to set this up
right in the Constructor right right
when we deploy this contract that's when
we're going to say okay okay these are
going to be the allowed tokens these are
the price feeds and that way it's going
to be like that forever right we're
never going to be able to change this so
what we'll do is in our Constructor now
we'll take in the the allowed tokens and
their price feeds right because in order
for this system to work go to like data.
chain. Link in order for this entire
system to work if we want to know how
much value our ethereum that people
deposit in here is worth we need to have
the pricing right the only way for us to
know if we're over collateralized if we
know the value of our eth in our Bitcoin
so we're going to use these two price
feeds to to maintain that because these
price feeds are going to be on different
addresses on different chains you
already know that we got to parameter
tize it so we'll do an address
array
memory token addresses comma address
array memory price feed addresses as
input parameters
and we're going to say token address
zero maps to price feed zero token
address one maps to price feed one Etc
and while we're in here I already know
that our DSC engine is going to need to
know about our decentralized table coin
why because our DC engine is going to
need to know to call burn and mint so in
here in the Constructor this is also
where we're going to pass the address
DSC address decentralized stable coin
address and so in here let's do some
vanity checks on this we'll say if the
token addresses. length does not equal
the price feed addresses price feed
address is price feed address is length
we have an issue right because if
there's more tokens or more price feeds
that means we mess something up so we're
going to go ahead and revert with a new
error go to our errors here we'll say
error DC engine uncore uncore token
address is and price feed addresses must
be same length a massive error I know
but I like being verose like I've told
you before so if those don't match we're
going to go ahead and revert then we're
going to Loop through the token
addresses and update our mapping that we
just created here to say okay the token
address is map to the price feed address
now in order for us to get a pricing
we're going to have to use the US D
price feeds and everything every price
feed that we're going to have to use is
going to be the USD back price feed so
for example it's going to be eth USD BTC
USD mkr USD Etc okay so we're going to
Loop through we're going to say four U
and 256 I equals z i is less than hen
addresses. length
i++ so we're going to Loop through this
token addresses array
and we're going to say sore price
feeds of token
addresses I is going to equal to price
feed addresses of I so we're going to
set up this price feed so whatever the
token so the token of I is going to
equal the price feed of I and that's how
we're going to set up what tokens are
allowed on our platform if they have a
price feed they're allowed if they don't
they're not allowed and then I know
we're we're going to do a lot of stuff
with our DSC so this is where this is
definitely going to be an immutable
variable so we can scroll up make a a
mutable variable we'll say so we'll say
decentralized stable coin private iore
DSC because we're going to make this
immutable and since we're using
decentralized stable coin we're going to
have to go ahead and import
this so let's go to the top here do
import decentralized stable coin from
decentralized stablecoin Soul so now
decentralized stable coin private idsc
you can now do oh excuse me this is
going to be private
mutable go bit down to the Constructor
we're just going to say idsc equals
decentralized stable coin DSC address
like that again using GitHub copilot if
you don't have GitHub co-pilot that's
okay there's a lot of other free AIS
that you can use as well Okay cool so we
set up our Constructor we're going back
down down to deposit collateral whole
reason we were doing this is we're
saying okay we should only allow certain
kinds of collateral on our platform so
now we can create a new modifier called
is allowed token where we can just say
if sore price feeds of token equals
equals address Zer then we can go ahead
and revert with a new oh that looks like
a good one DSC engine token not allowed
I'm just going to copy that go to the
top and thanks get up copilot just
autofilled it in for me okay great and
then of course we need to do this down
here Co so is token allowed more than
zero is allowed excuse me is allowed
token
token
collateral address like this all right
cool and then additionally I'm going to
add a non reentrant modifier here as
well we're going to grab this from open
Zeppelin whenever we're working with
external contracts it might be a good
idea consider making your function
non-reentrant
re-entrancy
and to be honest sometimes I'll just rip
a non-reentrant modifier even if I'm
pretty certain it's not vulnerable to
re-entrancy attack I feel like to be
honest most functions should be non-
reentered by default but especially when
working with external contracts it's a
good idea to maybe put this modifier
here now this might go to audit and we
might say hey well we don't need this
non-reentrant modifier and maybe we get
rid of it but maybe we don't the
trade-off is it's a little bit more gas
intensive to have this here but it's
also safer so I'm just going to stick it
in here even if I'm pretty certain I
don't need it we're going to get this
from open Zeppelin has a non- reentrance
a nonreentrant
modifier from their re-entrancy guard so
we'll import actually my GI co-pilot
automatically had it reeny Guard from
open Zeppelin contracts security
reentrancy guard. Soul yep that's
actually exactly correct and then what
we can do is scroll down and we see our
DSC engine is re-entrancy guard by doing
this we now have access to this
non-reentrant modifier and now this
function is non-reentrant which is what
we want okay cool now we can finally
start doing some collateral stuff so
we're going to go ahead and deposit this
collateral first first thing we're going
to need to do is a way to track how much
collateral somebody has actually
deposited so what's that look like well
that probably looks like a mapping to me
so let's go to the top we'll create a
mapping of address user
to mapping of an address token to un 256
amount private
sore
colateral the
posited so this is a mapping to a
mapping crazy right so we're going to
map the user's balances to a mapping of
tokens which is going to get mapped to
the amount of each token that they have
all right so let's scroll back down to
our deposit collateral function so now
we have a collateral deposited mapping
so we can do s collateral deposited of
message. sender of this token collateral
address now that we know it is an
allowed collateral address it's going to
be plus equal to the amount collateral
right and and I'm actually run into an
issue here where when I do Forge format
formats the code look like this but
right now when I'm saving it's
reformatting it a different way so what
I'm going to do is it looks like it's
using a different formatter that I don't
like so I'm going to go to the
extensions solity hard hat hit the
settings here extension settings and
we're going to change this from prettier
to forge because I want to use the forge
format settings now if I hit save okay
great it saves and formats the way I
wanted to okay great so as you can see
here we're updating State and what
should we do when we update State we
should emit an event so we're going to
emit
collateral deposited we're going to have
it be the message. sender who is
depositing the token collateral address
and the amount collateral as well which
means that that we have our first event
all right so let's go on up here Zoom
back out scroll all the way up to the
layout where do events go events go
after the state variables okay so I'm
going to copy this it go right after the
state variables before the modifier so
we're going to say events we're going to
go here we're going to say
event and get up co-pilot automatically
filled it in for me awesome address
index user address index token un 256
amount we don't really need to index
that maybe we do why not let's just
index it all right cool and just to know
a keyboard shortcut that I use a lot is
control back or control minus which
allows you to go back to the last spot
you were in your code if you do control
shift minus it'll go forward and I use
this all the time so for example if I'm
way down here in my code and I hit
control back I'll just go right back to
the last place my cruiser was and I use
this all the time I'm not sure what the
keyboard shortcut on Windows or linuxes
but on a Mac it is control back and
control shift back and I use it all the
time anyways so we are in deposit
collateral we have this emit here we
finally have the event and Okay cool so
we're updating the collateral internal
recordkeeping we're admitting a vent now
of course we should actually get the
tokens right and you can see here we're
following CI right so you do a little
notice follow follows CI checks effects
interactions so all the checks are
happening in our modifiers here these
are all the checks our effects are right
here and then finally our external
interactions so this is where we're
going to do that transfer from and we're
going to need to wrap our collateral as
an erc20 so we're going to need to call
transfer from on it so I'm going to go
ahead and import
ic20 from at open zeppin contracts SL
token
erc20 erc20 doou contrl minus to go
right back down to here isn't that nice
and we can finally do ic20 of the token
collateral address do transfer from
message. sender to address
this
amount all like this and we'll say so
this function actually returns a Boolean
full success and we want to make sure
that this is actually being true so we
want to say if not success we'll just
revert DC
engine uncore transfer failed like this
what's this an error you bet it is so
we're going to put this at the top erir
DSC engine transfer failed and I'm going
to hit control minus to go right back
down to the code that I was working with
Okay cool so this function look looks
pretty good to me I'm able to deposit
collateral in here and update our
mappings now might be a good time for us
to start writing some tests for this
right and we could do something similar
to what we did before where we just kind
of threw together a real quick setup in
our unit tests and then had our
integration tests be what our deploy
script actually uses well let's write a
couple more functions first and then we
can go ahead and and do all that
work so we have a way to deposit the
collateral okay awesome what should we
do next well the next thing that we
should do is have a way to Mint our DSC
token right once they deposit the
collateral they should be able to Mint
the DSC token and then the combination
of those two will be this function
deposit collateral and mint DSC so let's
go ahead and actually create this mint
DSC function because now that they have
some collateral we should be able to
Mint some DSC and now this is actually
going to be a surprisingly involved
method right cuz in order to Mint DS C
we need to check if the collateral value
is greater than the DSC amount and this
is obviously going to involve a number
of things right it's going to involve
price feeds we're going to be checking
values we're be checking a whole lot of
stuff okay so this is actually going to
be a little bit more of an in-depth
function here so let's create this mint
DSC function and we'll have this get
passed in a un 256 amount DSC to Mint
people can pick how much DSA they want
to Mint so let's say for example someone
deposits $200 worth of eth maybe they
only want to Mint $20 worth of DSC right
so they can pick how much they want to
Mint here so amount to Mint here and
let's add some checks here we should
have the mint more than zero Mount DSC
to Mint this what else we probably want
this to be non-reentrant even though we
probably don't need this to be non
reentrant because it's our C token but
let's put it in here anyways it's
probably all we need for now we probably
want to do
some looks like get up co-pilot even
gave me some some follows CI yes oh
that's wrong amount DEC to Mint amount D
to Mint the amount of
decentralized stable coin to Mint at
notice they must have more collateral
value than the minimum threshold we're
going to figure out what that means in a
second so mint DSC we'll need to keep
track of how much everybody has minted
right so whenever they're minting DSC
they're in a way they're minting debt
right so we're going to need to keep
track of that where can we do that well
we can do that as a state variable so
we'll do mapping of an address user
mapped to their U 256 which is going to
be an address user map to the u u
256 Mount DSC minted it's going to be
private
sore DSC minted this now I can go back
down here this new mapping SDC minted
message. sender plus equals amount DSC
to Mint so we're going to keep track of
all that they minted again this is going
to follow CI
so now we want to do a little check here
if they minted too much for example like
they minted $150 or $150 worth of DSC
but they only have $100 worth of eth
that's going to be way too much we
should 100% revert if that happened so
I'm actually going to make a function
going to make an internal function
called revert if Health factor is broken
with the message. sender being sent so
we're going to create this new internal
function so up here we have external
functions we're going scroll down here
we're going to make
this private and internal
functions this this and we're only going
to be able to call this if we're only
going to be able to call this internally
right so we're going to create this
function function revert if Health
factor is broken address User it's going
to be an internal View
function and we're going to basically
one check Health factor which is
basically do they have enough collateral
right and then revert if they don't have
a good health Factor so this health
factor is actually a term that I
borrowed from the a documentation reach
wallet these risk parameters enable the
calculation of Health factor and you can
see a little bit of an image here that
shows it so additionally we're going to
need to make a function that checks the
health Factor actually these are going
to be private and internal view
functions so first we need to make a
function that can get the health Factor
so we're going to make another function
here called function underscore Health
Factor we're going to take an address
user and you see we have this leading
underscore to tell us as developers that
this is an internal function so let's
actually give this in underscore as well
function
oops Factor this will be a private view
going to return a u into 256 so what
this health Factor thing is going to do
give it a little bit of Nat speec yeah
we can say this is going to returns how
close to liquidation a user is say if a
user goes below one then they can get
liquidated so we're going to figure out
what the ratio of collateral to DSC
minted that users can have with this
health Factor function we're going to be
building a lot of stuff here so in order
to figure this out what do we need well
we're going to need to get both their
total DSC minted right and we're going
to need to get their total
collateral value right not just the
total collateral we're going to need to
get the total collateral value make sure
the value is greater than the total DSC
minted
so we're going to have to make another
function we're going to need to get the
UN 256 total DSC minted and the UN 256
collateral value in USD we're going to
create a new function called get count
information from that user so we're
going to do another one boom function
uncore get account information like this
we're going take an address user this
will be a private View and we're going
to
return two things U and 256 total DSC
minted and U into
256 collateral value in USD two
functions here to get the total USD
that's easy right we just do total DSC
minted equals this array this excuse me
this mapping that we just created right
we're keeping track of this exactly so
all we got to do is this to get the
total collateral value in USD we're
going to need to do some more math so
I'm going to say collateral value in USD
equals get account
collateral value and we're going to pass
some user so this is going to be a
different function and we're going to
make this a public function so that
anybody can call this function
themselves so we're actually going to
copy this private and view internal
functions public internal View and then
we're we're going to do View and pure
public and external functions all the
way at the bottom we'll say private
instead of we'll say public and external
view functions we'll do function get
account bilateral value address of the
user this will be a public view so that
anybody can call it returns a uint 256
stay with me I know we're kind of going
down this tree stay with me here we're
about to go even further now to get the
actual value what do we need to do well
we need to Loop through each collateral
token get the amount they have
deposited and map it to price to get the
USD value so we're going to need to Loop
through all the collateral tokens uhoh
do we have a way we can do that let's
scroll to the top well we have a mapping
of price feeds but we don't have a way
to Loop through them so what we can do
is we could just have like you know
address we and then address address
wrapped BTC we could just have two
tokens like this we would have to Loop
through anything we're going to make
this system a little bit agnostic so
that you could deploy this with any
amount of stable coins any amount of
collaterals so we're going to make a new
state variable it's going to be an
address array private sore collateral
tokens and what we're going to do is
right in our Constructor when we update
our price feeds with the token and the
price feed we're also going to add the
tokens addresses of I to this array now
we have this array of collateral tokens
that we can Loop through and that way we
can calculate how much value people have
based off of all of the tokens so get
account collateral value we're going to
say four U and 256 I equals z I is less
than S collateral tokens. length i++
address token equals s collateral tokens
of I and we'll get the amount so this is
the token that we're working with we'll
get the amount by un 256 amount that is
deposited by this user by sore
collateral
deposited user token like this then
total
collateral value in USD plus equals
uh-oh now that we have the amount we're
going to need to get the USD value of
this and this is probably a function
that we're going to want to have public
so that other people can use this as
well so create a function get USD value
where somebody passes in an address
token U and 256 amount this will be a
public view returns U 56 and this is
where we're going to do some price feed
stuff some stuff that's very familiar so
we're going to need to get the price
feed for the token and then times the
amount by the price so we're going have
to work with aggregator V3 interface
again that chain link data feeds so
import and I know we've worked with this
before
aggregator V3
interface
from it looks like get up co-pilots got
my back at chain link contracts blah
blah blah this means we're going to have
to install that remember we can install
the smaller one with Forge install Smart
contract kit SL chainlink brownie
contracts at
0.6.1 D- no
dashit this chain Le brownie
contracts awesome then we're going to
have to go to our founder.
add some remappings in here so we're
going to put a little comma we're going
to say at chain link SL contracts equals
lib slash chain link brownie contracts
SL contracts like that so now we have
the aggregator V3 interface scroll down
to the bottom get USD value I know we've
done this before I know this is familiar
to a lot of you but we're going to say
aggregator V3 interface price Fe
equals aggregator V3 interface of sore
price feeds of the token so we're going
to get the price feed of the token we're
looking to get the value of now we have
the price feed here we're going to do
this WOW get a co-pilot spot on we're
going to get the price by calling Price
feed. latest round data and this is
where we're going to do a little bit of
math right let's say 1 eth equals
$1,000 The Returned value from CL will
be
1000 Time 1 E8 how do I know this well
if I go to docs. chain. link go to data
feeds I can scroll down to price feed
addresses and I can see if I do show
more details for
ethusd eth-usd it has eight decimal
places and I know Bitcoin USD also has
eight decimal places we could add some
code in here so that we make sure we're
getting the right decimals but I'm just
going to go with eight for now and now
we can do a little bit of math here so
we're going to need to do the price
times the amount right but is this all
we need to do turn price times amount no
because the number is going to be way
too big right let's say the price is a
th000 times 1 E8 and now we're
multiplying that by let's say the the
amount is 1,000 times 1 E18 because it's
going to be in way this number is going
to be absolutely massive right so first
we need to multiply this a th by a
number to get this number to match this
one so these need to be the same units
of precision so that's going to be we're
going to have to multiply this first by
1 E10 and because I don't like mic
numbers we're going to scroll to the top
create a new state variable right on
here we're going to do a u 256 private
constant additional feed
Precision equals 1 E10 like this and now
it's not a magic number so instead of
doing this now we're going to say okay
price and we're going to wrap it as a u
into 256 so that everything's down 256
we say the price first needs to be
multiplied by the additional feed
Precision so that now both of these are
U and 256s and they both have 1
E18 but then we're going to have to
divide all of these by 1 E18 as well so
this number doesn't look super wonky so
we're going to have to wrap this whole
thing by 1 E18 and as I hate floating
magic numbers like this so we're going
to scroll back to the top copy paste
this this is now just going to be called
Precision 1 E18 grab Precision divide by
Precision so u56 price times additional
feed Precision Time amount divided by
precision and we should be good to go
here this is where my brain immediately
goes okay definitely need to write some
tests for this so once we finished going
through this we're definitely going to
read we're definitely going to write
some tests at least for get USD value
here so let's go back up through our
massive tree of functions that we just
created that are not complete so let's
go back to get account collateral value
and this is going to be the total us the
total collateral value in USD it's going
to be get the USD value of the current
token we're on
times the amount that we're working with
toal Cal value in USD actually let's put
that there boom and that's it right so
we just Loop through all the tokens in
the token array and we just add up the
value and USD of each one of these
tokens and I know we don't need a return
here but I'm going to add a return here
anyways return this so cool so now we
have a way to get the collateral value
in
USD we needed that way up here oops
let's do collateral value in
USD get clonal value oops let's do this
and awesome so now our get account
information is going to return the total
USD minted the total DSC minted and then
the total value of all the collateral
here okay
great and we can scroll up again we have
this revert if hell factor is broken
which is still busted because this
function doesn't do anything but now we
can actually have it do something
because we have this health Factor here
we're going to update our health Factor
function because now we have the two of
these what we can do is we can just get
the ratio of these two so we could say
flatal value in USD divid by total USD
minted this is what we're going to
return for a health Factor right well
not quite so let's say we minted we have
100 collateral divided 100 DSC right
this is one: one if we go down a we go
down a penny we're going to be under
collateralized right and we don't want
to go under collateralized we always
want to be over
collateralized because if this ever goes
below 100 our system is is bunked up
right so we want to set the threshold to
be like hey if you go under
150 collateral you can get liquidated
right if we go under 100 it's already
too late so we want to say hey we want
you to go at least 150 so we're going to
create a liquidation threshold and we're
going to do this at the top so we're
going to say unit 256 private constant
liquidation threshold equals 50 and this
means you need to be 150 or no this
means you need to be 200% over
collateralized I think might be 150 but
we'll find out in the test so now if we
go back down to where we were to get our
health Factor we're not just going to
divide these two and even this doesn't
really work because if we have 150 over
100 150 divid 100 is equal to 1.5
decimals don't work in solidity so it
would just be one I guess that would
work but we want we want to know exactly
what the health factor is right with
Precision so first off let's instead of
just doing this let's say U into
256 collateral adjusted or threshold
equals the collateral value in USD times
liquidation threshold and then we should
divide by 100 right because the
liquidation threshold has is multiplying
it's making our number much bigger so we
should divide by 100 as I don't like
floating numbers so we'll do U into 256
private constant
liquidation Precision equals 100 so
we'll go back back down divided by the
liquidation Precision so now we have
this collateral amount adjusted for this
this threshold right so now you can kind
of think of it as instead of say we have
$150 of eth divided by $100 of DSC right
this would be 1.5 but now they need to
multiply by 50 as well but now this
collateral value is multiplied by
essentially 1 over5 right let's let's do
the math here right if we had say $1,000
of eth we just times that by 50 which
gets us to ,000 but then we divided by
that by about 100 which equals 500 so if
we had $1,000 of eth * 50 was this
divided 100 is 500 same thing with this
this example down here if we had $150
worth of each eth we say 150 * 50 equal
75 / 100 equal 75 and then if we do 75
ID 100 that is going to be definitely
less than one right so we we're
basically saying with this 50 threshold
50 over 100 is essentially 1 over two
we're saying you need to have double the
collateral in here so yeah so now that
I'm talking it out loud this 50%
liquidation threshold means we need to
be 200% over collateralized right we
need to have double the collateral that
we have the mint
DSC anyways so whole bunch of math
hopefully this makes sense if not
definitely work with your AI to make
sure this makes sense or ask questions
in the discussion right I know some of
this math can get a little bit tricky
here so collateral adjusted for
threshold and now we can return the
collateral adjusted oops this collateral
adjusted for threshold times Precision
divided by that total DSC minted now
this will give us our true Health factor
and if this is less than one you can get
liquidated now this is one example right
let's look at another example let's say
I guess this is two examples so let's
say they have $1,000 worth of eth and
100 DSC right so let's do the math here
1,00 * 50 equal 50 1 2 3 ID 100 is equal
to 500 500 divid 100 which is definitely
greater than one right 500 divid 100 is
5 so this person with $1,000 of e
deposited and 100 DSC minted would have
a health factor of 500 nice so now that
we have a health Factor we can actually
finally do this revert if Health factor
is broken function where we say we can
even put this above this as kind of like
a pseudo natspec we could say U into 256
Health Factor
equals underscore Health factor of the
user and we say if the user excuse me
actually let's do user Health factor and
if the user Health factor is less than
the some Min Health factor which is
going to be one Health Factor so let's
go ahead and create this let's go to the
top because we hate floating numbers you
into 256 private constant Min Health
Factor equals 1 if the user Health
factor is like than the Min Health
Factor then we're going to go ahead and
revert I don't love this we're going to
do DSC
engine underscore underscore breaks
Health Factor we're going to pass in
this health factor that we break with
boom new air scroll the top Air engine
breaks Health Factor un 256 Health
Factor looks like we still have some red
here what did I forget okay revert of
Health factor is broken underscore like
this boom now it looks like nothing's
red let's just make sure Forge
build successful
nice okay where were we mint DSC okay
mint DSC so we added some more DSC
minted and if adding this DSC breaks the
health Factor because breaks the health
Factor we should revert we should not
let anybody mint DSC if they're going to
cause themselves to get liquidated I
mean we could go ahead and let them do
it but like let's not because that's not
a very good user experience now what we
want to do is we want to actually mint
the DSC so this is where the DSC has
this mint function that's only owner and
the owner of this is going to be the DSC
engine now we could say and if we look
at this mint function it returns a
Boolean so we'll say bull minted
equals iore DSC mint and what does it
take for parameters address 2 and amount
so address two is going to be message.
sender and amount is going to be amount
DSC to Mint and then we'll say if not
minted we'll just say revert DSC engine
uncore uncore mint failed which is a new
error scroll up to the top error DC mint
failed yeah
Co so now we have a mint function and we
have a deposit function so we can
deposit collateral we can mint DSC but
additionally we can get account
information we can calculate someone's
Health Factor we can calculate the USD
value of these different tokens so at
this point I'm like o I have no idea if
what I'm doing makes any sort of sense I
want to make make sure I write some
tests here so this is where we could go
ahead and create a new folder unit tests
and if you wanted to you could skip
writing the scripts and just kind of
deploy in your unit tests and then do
some integration tests but I'm just
going to have my unit tests also be my
integration test for this one so let's
go ahead let's write a script deploy
DSC s.o you already know the drill for
this
spdx like since I even zoom in a little
bit identifier MIT contract deploy DSC
is script import script from Forge
STD script. Soul like that pragma
solidity
0.818 it's good in here we're going to
have our function run external we've
done this a 100 times exter
n and this is going to returns a couple
of things it's going to both return the
DS
decentralized stable coin and the DSC
engine and it's going to return
something else but I'm not going to put
it in quite yet so to do that we're
going to have to
import centralized
stablecoin from
slsrc centralized stable coin. we're
going to close and reopen my vs code
it's being really weird right now
there we go all fixed so the two of
these what are we going to do well we're
going to do vm. start broadcast this.
stop
broadcast this oh and we need the DSC
engine import DSC engine from srcds
engine. Soul and in here we're going to
deploy both of these so we'll say
centralized stable coin DSC equals
new decentralized stable coin does our
decentralized stable coin have any
parameters it does not we're also going
to deploy our
dsce our DSC engine engine equals new BC
engine this takes a whole bunch of stuff
right this is going to take go to the
Constructor token addresses price feed
addresses if we toggle oops tle the word
wrap addresses price feed addresses and
the DSC address so we have the DSC
address boom it's going to be this one
where do we get the price feed addresses
and you guessed it we're going to make a
helper config so what does this DSC
engine need Constructor an array of
token addresses an array of price feeds
and then the DSC address so where are we
going to get those addresses from you
guessed it a helper config so new file
helper configs do Soul
spdx license identifier MIT pragma
solidity
0.818 contract helper config is script
import
script from Forge
script. soul for now let's do this on
the sepolia Chain so we'll do
struct Network config do we need in here
we're going to need an well we're going
to need we and wrapped Bitcoin those
price feed addresses and those DSC
addresses address we USD price
feed address wrapped Bitcoin USD price
feed again we is the erc20 version of
ethereum I've got a example we token
contract on zolia and if you look at it
right here go write contract it's got
this function deposit where you deposit
eth and it'll return to you an erc20
version of eth to your metams called we
then whenever you're done with it you
just withdraw your eth and burn your we
WRA Bitcoin is something similar but
with Bitcoin the difference is since
Bitcoin doesn't originate on the eth
blockchain there is some risk in
bridging it over but I'm not going to go
into that that's something for you to
look up so we're also going to need the
address of the we token itself we're
going to need the address of the wrapped
Bitcoin itself and we're going to need a
un 256 deployer deployer key kind of
like what we did in one of our previous
lessons we're going to have Network
config public Active network config and
then we're going to have
Constructor like here we're going to
have a function get sepolia e
config public view returns Network
config memory this and then we're going
to return Network config we're going to
have we USD price feed B let's go to
docs. chain. link let's go to theia
zolia where's
ebtc eth excuse me e USD right here copy
that paste
we're going to need W Bitcoin USD price
feed on seoa bitcoin USD right here grab
that need the we contract address which
I have here this is one that I deployed
we here we're going to need wrapped
Bitcoin and if you're looking for all of
these and you want to just copy them out
of the GitHub repo associated with this
you can go to
SRC you can go to script upper config
and they're all in here if you if you
want just copy paste by the way but also
wrapped Bitcoin which I guess is this
address thought I had a different one
well whatever we're going to use this
one and if it doesn't work that's fine
we're going to use this one then of
course player key bm. EMV unit private
key like this okay and then we're going
to do function get or create envil eth
config public
turns Network config
memory and we're going to do a little
bit of mock deployments here but we're
going to say if Active network config do
wusd price feed does not equal to the
zero address then we've already set it
turn Active network e config we're going
to do some broadcasting so we're going
to need a couple of couple of mocks in
here we're need some mock price feeds
and some mock ec20 tokens so we're going
to need a mock B3 aggregator which we're
going to go to test folder mocks and I'm
going to copy paste a mock from this
repo if you want to copy paste as well
go for it test
mock mock V3
aggregator
this new file mock V3
agregator doou cool import that import
mock B3
agregator fromt dot slash test
slmm V3 aggregator Doo so we have that
we're also going to need some moach Er
c20s we can get those actually directly
from open Zeppelin so if we do import
irc2 mock
from atop Zeppelin contract slmx
erc20
mocko and if we command click into this
or you open this up you can see there's
a whole bunch of stuff in here like we
can mint as much as we want burn as much
as we want do transfers and stuff we can
do pretty much whatever we want and
that's why it's a mock token good for
testing with so down here vm. start
broadcast we're going to create a mock
B3 aggregator ethusd price feed equals
new Mach V3 aggregator and what does
this take for the Constructor takes
decimals and an and an initial answer so
we're going to scroll up here we're
going to say UN 2 or excuse me U 8
public constant
decimals decimals equals 8 and we'll say
U or not U me int 250 D6 public constant
eth USD price equals
2000 E8 and then we're going to do the
same thing but instead of eth it's going
to be BTC and we'll make this 1,000 to
E8 eth USD scroll down all right new
mock what does it take control click U 8
decimals initial answer control minus to
go back
decimals and then the initial answer
those okay oh and let's do vm. stop
broadcast now we're going to do erc20
mock we mock equals new erc20 mock what
does this one take name symbol initial
account initial balance okay say we we
message. sender
1008 we probably want to do more than we
probably don't want to have these
floating numbers in here but it's just a
mock it's not a big deal I guess now
we're going to copy paste all of this
for
BTC
BTC we're going to say
BTC it's going to be the wrapped
BTC wrapped
BTC wrapped BTC like that stop the
broadcast and then return Network config
we USD price feed is going to be address
oh thanks get up
co-pilot thanks get up copilot thanksg
up co-pilot thanks get up
co-pilot and this is actually going to
be the default Anvil key which if you
want you can just go back to here again
and copy paste it out of here or you
know what we just run Anvil scroll up
boom private key right here cancel that
say U 256 public excuse me int 26 public
default Anvil key equals B that in and
we'll say we just use the default Anvil
key if you're working with Anvil okay
nice so now we have get or create Anvil
get zolia let's update our Constructor
so we'll say if block. chain ID equals
equals 111
55111 then active Active network config
equals get Suppa eth config else to
Active network config equals get or
create eth Anvil config nice so we've
got a little bit of a helper config here
a little semicolon down here this looks
pretty good what's wrong here sorry this
is a
ent6 okay cool so now that we have a
helper config we can go finally back to
our deploy DSC let's import that in here
import helper config from helper config
right at the top we'll say helper config
config equals new upper config and out
of this config we're going to get all
this we Bitcoin we repid coin deploy key
Etc so I'm going to say address with USD
price feed oh I can even just hit Tab
and looks like it has most of it yep W
USD rep Bitcoin we rep Bitcoin deploy
key equals config do Active network
config cool looks good to me I'm going
to toggle word wrap so that it wraps
around okay cool we have all of those
now our DSC engine takes an array of
token addresses an array of price feeds
so we can say right at the top let's
make those arrays we'll say address
array public token
addresses address array public price
feed addresses and we'll say token
addresses equals we and rep
BTC price feed addresses equals we USD
price feed address W Bitcoin USD price
feed okay cool I think that's everything
right yeah so now we can go back to this
line now DC engine engine new DC engine
and it takes the token
addresses price feed addresses and DSC
okay cool and then finally something we
haven't really talked about too much but
this decentralized stable coin like I
said it's ownable but it needs to be
owned by the engine so this ownable
actually has a transfer ownership
function and we're going to call that to
transfer ownership to the engine so
we'll go back to our deploy here oh
sorry this isn't DSC me address DSC so
then we're going to call
dc. transfer ownership to the address
DSC engine now only the engine oops
engine excuse me only the engine can do
anything with it and then we're going to
return all these return DSC and engine
nice oh and the deployer is going to go
here okay cool so the reason we did all
this was because I wanted to write my
unit tests using actual deploy scripts
because I prefer to do that but like I
said it might be a good idea for you to
write unit tests before you write your
deploy scripts and then integration test
with your deploy scripts but in any case
let's go ahead and finally create a test
in here DSC engine test.
T.O remember the whole reason we're
doing all this I know we've been coding
a lot is in the DSC engine we added a
ton of functions in here some of them
like get USD value which we definitely
want to check get collateral account
value we want to make sure minting Works
our Constructor Works depositing Works
Etc so we're just kind of testing as we
go along which like I said when I'm
actually coding this I did write tests
and I did write deploy scripts because I
did want to test as I was going right I
didn't want to have to go back and
refactor and rewrite my code if I made
some glaring mistake right it's really
good to test while you're building as
well and to be honest I I feel like it
makes me go faster because I have more
confidence that what I did was correct
while I'm coding so
spdx
license identifier MIT pragma solidity
0.
8.18 little car here contract DSC engine
test is test import test from Forge STD
test that's all like this all right cool
function
setup public or external we're going to
need to deploy to deploy our contract so
we're going to import deoy DSC from do
script SL deploy dc. s.o deploy DC
deployer we're going to say deployer
equals new deploy DSC like that again
I'm using a lot of tabs here and we're
going to say we're going to need the
import de centralized stablecoin
from SRC decentralized stablecoin doou
we're also going to need to import the
engine the scce from again this is where
get up co-pilot could really make your
life a lot easier just being able to hit
tab here or just whatever AI that you're
working with now we're going to say DSC
let's actually make these we'll say
decentralized table coin
DSC and DSC
engine we'll call
dsce that's confusing you call this like
engine or something I'm going to call M
dsce so now our deploy returns DSC and
the engine so we're going to say return
DSC
dsce equals deployer run okay that looks
pretty good there's a bunch of stuff
more for us to do but at least we have
our tests set up here so one of the
first tests that we want to do is this
price feed test right we want to make
sure this get USD value this math that
we're doing here because we're doing
some weird math stuff we want to make
sure this is actually working correctly
so I'm going to do I'm going to set up a
little price feeds Test Section price
tests like this we're going to say
function do test get USD
value and here we're going to test our
get USD value function so it gets past a
token address and an amount so we're
going to need to get those tokens that
we use to deploy this we can get that
pretty easily from our helper config so
what we can do actually back in our
deploy we can also have this return the
config
and just at the bottom we'll also have a
return
config I'm a config and config will be
the helper config helper config config
import that import helper config from
dot dot
script fig.
s.o cool and now we can get the
we address and we can also get the e USD
so we'll put those up at the top two
we'll say address ethusd price feed and
we'll say address we get this from the
he helper config so those are the first
two so it's eth USD price feed bitcoin
price feed goes here so comma we comma
this is the Bitcoin token comma this is
deployer key equals config
do Active network config
cool we have the price feed and we have
we so now we can finally go down here we
set this function up so we'll say un 256
eth amount equals let's say there's 15
eth right 15 eth if we have 15 eth times
it by
$2,000 per eth equals what
30,000 30,000 e 18 right real simple
simple math so let's do that you 6
expected USD equals 3 301 2 3
$30,000 and we'll say U into 256 actual
USD equals dsce doget USD value we and
eth amount and the reason this should
work is because in our engine we pass
the token and the amount and internally
it uses the price feed associated with
that token calls the price to get the
amount and now we should be able to just
do assert equals expected
USD and actual USD all right I know
there's a lot of setup just to write
this one test but like I said I like
making sure my deploy scripts are part
of my test Suite right from beginning
but it might be a good idea to just do
them as integration test so Forge test-
m test get USD
value and it works now I will point out
the first couple of times that I ran
this test I actually failed miserably I
got a number of things wrong and that's
okay because you will and that's why you
write tests so I also while I'm here
let's also write at least one deposit
collateral test so let me copy this
paste it here because we're going to
write a lot of deposit collateral D
deposit
collateral test make it look a little
bit pretty at least
that we can do some more simple tests
like function test revert reverts if
collateral zero
public uh we'll prank a user so up at
the top we'll do an address public user
equals make ADR
user like this user
Capital
user let say vm. start prank or user now
we'll at least approve the token can go
to the protocol so we'll do erc20 mock W
do we have that imported nope we're
going to have to import that import
erc20 mock from atop Zeppelin SLC
contracts slash what is it MOX slash or
contracts MOX erc20 mock okay ec20 mack.
Soul ec20 mock we do
approve uh address
dce some amount let's do at the top
let's make another U 256 public constant
amount
collateral equals let say 10 ether worth
of
collateral down here we'll approve that
10 collateral and then we'll do vm.
expect
revert with DSC engine dot we're going
to need to use that needs more than zero
in here needs more than
zero do selector and now you guys know
what the selector bit means dsce do
deposit colat
collateral say we zero this and then vm.
stop Rank and actually this might fail
for a different reason but let's go
ahead and try it Forge test DM
oops okay cool and this actually did
pass now if we want to make this a
little bit better of a test we should
also mint our user some weth and we
probably should do that right in the
setup so we don't have to do that for
every single test what I'm going to do
is I'm going to do erc20 mock
we mint user we're going to do a 2 into
256 a constant starting here see
20
balance
balance
equals and let's say this is 10 ether as
well 10 ether starting year20 balance
boom all right cool so now Forge test
all of our tests are passing cool and
like I said I'll do this kind of as a
sanity check to make sure that my
architecture is even making sense right
so we what we probably want to do next
then is have a test for collateral is
being deposited in these data structures
but for now I'm content with these tests
so I'm just going to go back to writing
my contracts like I said there's no one
single process and I don't think I've
ever written a smart contract completely
in one go I'm pretty much always writing
tests as I'm writing the code so it is a
really good idea to to do this yes you
do not have to write the deploy script
as you're writing your code but
something that I like to do and then you
know what while we're writing these
tests let's also do D- Fork URL sepolia
RPC URL let's also do this because this
probably will fail actually because we
can't just mint we at a thin air and we
do indeed fail test get USD value oh
interesting that's the one that fails
test get USD value ah this one fails
because we're hardcoding the expected
USD right here here and of course the
price on sapoia is the actual price as
opposed to kind of this fake price that
we're making up so we should probably
update this test to make it more
agnostic right we probably should update
this test so that instead of just
hardcoding 3,000 in here update this
test to use the price of the actual
price feed for now I'm going to leave it
as is and then I can fix it later for
running those Fork
tests so where are we now okay so we
have a way to deposit collateral we have
a way to Mint we don't really have too
many tests here we're just assuming that
this kind of works for now which is okay
but this is good right we're getting
somewhere so we can mint our debt or our
DSC we can actually now we can get a
whole lot of information as well which
is awesome let's now combine these two
into kind of this main function that
we're thinking a lot of people are going
to call this deposit collateral in mint
USD right the purpose of this protocol
is to Mint this stable coin right
deposit collateral and mint DSC which is
just going to be the combination of
deposit collateral and mint DSC so in
here what this this is going to take
it's going to take similar stuff to
deposit collateral and address token
collateral address a u into 256 amount
collateral and then also a un 256 amount
DSC to Mint
right that and here we're just going to
call so deposit collateral is external
right now we'll make this a public
function so we'll change this to deposit
collateral or we give it the token
collateral address and the amount
collateral and then we'll call Mint DSC
amount DSC to Mint boom so it's all this
function does is just combines the two
of them oh and mint
DSC mint DSC is not defined because it's
external we'll make this public as well
so that our contract can also call it
toggle word rep put that back on okay
and cool so this is going to be one of
our main functions we're thinking so
let's add some Nat spec to it we'll say
at param this is where get co-pilot is
really helpful token collateral address
the address of the token to deposit as
collateral at Pam amount collateral yep
at Pam that looks good too and then
we'll at noce
this function will deposit your
collateral and and mint DSC in one
trans action right because otherwise
we're going to have to have people call
deposit collateral and then mint but
some people they're probably just going
to want to do both at the same time
because that's kind of the purpose of
this
protocol okay great so we have a way to
for people to get money in how do they
get their money out so we're going to
need to write this redeem collateral
right so in order for them to redeem
collateral let's talk about this in
order to redeem collateral they need
what one their health
Factor must be over one after collateral
pulled so we're going to want to put
some checks in here to make sure that
they have enough money in here and
that's kind of the main thing right
that's all we really need to worry about
so let's go ahead start right this so
first we should let them choose which
collateral they want so address token
collateral address and then obviously
the amount amount
collateral and we're going to want to
add this more than zero modifier in here
for the amount collateral we don't want
them to be sending accidental zero
transactions and because we're going to
be moving tokens around we'll just do
non- reentrance better safe than sorry
we can figure out later in kind of like
a gas off it if this is even needed now
I'm going to write this function as if
somebody redeeming collateral is the
only time they actually redeem
collateral however we're going to
refactor this in the future to make our
code much more modular there's this
concept in computer science called dry
don't repeat yourself if you find
yourself coding the same thing that that
should send off a light bulb in your
head going oh maybe what I'm doing isn't
the best practice so we're going to code
this one way and then I'm telling you
right right now we're going to go back
and we're going to refactor this in the
future but I want to code it this one
way first just to show you the process
that you'll probably go through and how
you'll probably refactor it when you
come across this yourself so we're code
this one way now let's do it we're going
to need to pull the collateral out and
we're going to update our internal
accounting so we have this
sore collateral deposited of message.
sender of the token collateral address
right this is the our internal
accounting how much collateral they've
added we're going to do minus equals
amount collateral so this is assuming
we're going to pull it out if they try
to pull out more than what they have
we're relying on the solidity compiler a
little bit to throw an error right if in
their balance they have 100 and they
subtract try to pull out 1,000 right
it'll revert because as of newer
versions of solidity they don't let you
do this unsafe math stuff which is
awesome it saved us a lot of hassle so
and then since we're updating State
we're we're going to emit an event let's
call it
collateral
redeemed we'll say it's message. sender
so from m. sender the amount collateral
uh the token
collateral address like this so we're
going to go to the top we're also going
to refactor this event but you'll
understand why later event collateral
redeemed address indexed user address
indexed token uint 256 indexed amount
like this okay control minus go right
back down to where we were okay amount
collateral oh what's wrong with this oh
these are
backwards okay cool now all we have to
do is return the money well how do we do
this so we want to follow CI right
checks effects interactions checks
effects interactions but we also want to
make sure the health factor is good
after collateral collateral is pulled
and this is where sometimes you'll see
CI be violated when I need to check
something after a token transfer has
happened sometimes you'll see this CI be
violated a little bit and what you could
do is you could do like calculate health
Factor after and then like simulate it
but a lot of people choose to not do
this because this is really gas
inefficient so what a lot of people do
is they just go ahead with the doing the
token transfer first and then checking
this and reverting if this happens and
that's usually fine though because we're
going to revert the transaction if it's
bad right so what we'll do is we'll do
this token transfer and then we'll make
sure the health factor is okay so you
know how to move tokens around so we'll
say bull success
equals ier 20 token collateral
collateral address dot we can just do
transfer instead of transfer from since
transfer is when you transfer from
yourself transfer from is when you
transfer from somebody else so transfer
and we're going to be sending it to
message. sender and we're going to send
amount collateral and then if not
success if if not success we're just
going to go ahead and revert DSC engine
we'll just do transfer failed like this
and then we want to make sure that the
health Factor isn't broken and we have
written a function that does that
already called revert if Health factor
is broken so we can just grab this go
back up to our redeem and just do revert
of Health factors broken for the
message.
sender okay cool like I said we're going
to refactor this very soon okay but it
looks like this is actually pretty good
for redeeming collateral now this
revertive health factor is broken is a
little bit Troublesome with just this
raw redeem collateral let's say I put
$100 in and then I mint let's say $20
worth of DSC put $100 worth of Ethan and
I mint $20 worth of DSC let's say I'm
done like I want to burn all my DSC and
I want to withdraw all of my eth well if
I try to redeem all my eth it'll break
right it'll break my health Factor so
what I need to do first is I need to
First burn back my DSC and then I need
to redeem eth so it's a kind of this two
transaction process here this kind of
stinks so let's turn it into a one
transaction process so we're going to
combine redeeming your collateral with
also burning your DSC which means we're
going to need to create a burn DSC
function and we're also going to
refactor this in a little bit but I'm
just going to write it as if this is the
only burn D function for now so let's
have people burn their DSC right this is
when they they say hey I'm done with
these tokens and this will reduce that
if we scroll up to the top we have this
mapping here SD cemented it'll reduce
this sdsc minted so essentially it'll
reduce their debt in the system so burn
DSC we're going to add some modifiers
here this should be more than zero
amount so we want to do a unit 256
amount so they can burn as much as they
want and then what we're going to want
to do is we're going to say sore DSC
minted of the message. sender is going
to minus equal amount so we're going to
remove that debt remove that DSC minted
then we're going to do a little bu
success equals iore
DSC transfer from
D message. sender to address this amount
and we could also send this to the Zer
address but we're going to just send it
to our address for now because the
decentralized stable coin erc20 burnable
has its own burn function and we're just
going to call the burn function directly
on the token itself but first we're
going to take it from them bring it into
our contract and then we're going to
burn it so if not success then we'll
revert
dsce transfer failed this and this
conditional is kind of hypothetically
unreachable because if the transfer
fails up here we're going to throw the
transfer from fail error but let's say
this DSC token is implemented wrong
great we kind of have this backup but so
they're going to send their DSC token
here then we're going to call IOD DSC do
burn amount now since we're burning DSC
question is do we need to check if this
breaks Health Factor well probably not
right because we're burning DSC we're
burning debt it's highly unlikely that
burning your debt removing your debt is
going to break the health Factor right
we probably don't ever need this but I'm
just going to add this in here for
now just as a backup in a gas audit we
can figure out if we actually need it I
don't think this will would ever hit and
this is where when I do go to an audit
when I do go to a security professional
I can make sure to point this line out
say hey I don't think this line will
ever hit and I'm thinking of pulling it
out what do you think right it's good to
call these out in your comments that
when you do go to a professional who
knows they can help you out figure this
out so for now we're going to put it in
here although it's highly likely we
don't even need this and we're going to
refactor this function pretty soon
anyways so we have this burn DSC fun
function we're going to make it public
because we're going to be burning DSC
and redeeming collateral at the same
time so now we have redeem collateral we
have burn DSC now we can write this
redeem collateral for DSC where we send
DSC and redeem collateral at the same
time and so in here we're going to say
address token
colateral
address U
256 amount
collateral U 56 amount DSC to burn we'll
have this be external so then we're
going to call burn DSC with the amount
of DSC to burn we're also going to call
redeem collateral so we're going to burn
the DSC first then we're going to redeem
their collateral with the
token collateral address and the amount
collateral redeem
collateral oh this is external let's
make this public let's go back and then
of course of course we should revert if
Health factor is broken but if we look
our redeemed collateral function
currently does this already so we don't
need to do that here so I'm just going
to put this comment redeem
collateral already checks Health Factor
right here then we'll add a little bit
of natspec here so at Pam boom that's
not even the right Pam collateral
address the
collateral address to red
RAM amount collateral the the amount
collateral to redeem RAM amount DC to
burn the amount of DSC to burn this
function Burns DC and redeems underlying
collateral in one
transaction okay cool are we going to
refactor these two functions soon yes
absolutely but I want you to understand
why we're going to reactor them so we're
going to leave them in as they are for
now
Okay cool so this is looking pretty good
so we have a lot of stuff in here uh we
have deposit collateral and mint DSC so
people can mint our stable coin by
depositing collateral people can just
straight up deposit collateral people
can then redeem their collateral for the
US for the DSC that they minted they can
just straight up redeem collateral they
can just straight up mint DSC so long as
they didn't break the health Factor they
can burn DSC to to go help their health
factor and I don't think this line will
ever hit we've got to do a couple of
more things here most importantly we got
to do this liquidate function so this
liquidate function is kind of the the
key thing that holds this whole system
together if we do start nearing under
collateralization we need someone to
start liquidating positions removing
those positions we need somebody to
basically call redeem and burn for you
if your health Factor becomes too poor
right because the worst thing that would
happen is let's say there's $10000 worth
of eth backing $50 worth of DSC and then
the price of eth Tanks to $20 right $20
of eth backing $50 of DC well now the
DSC isn't worth $1 right the DSC then is
going to be worth you know whatever 20
over 50 is so we can't let this happen
right we need to make sure we liquidate
people's positions we remov people's
positions in the system if the price of
the collateral tanks okay and this is
where liquidation comes in so we say if
someone is almost under
collateralized we will pay you to
liquidate them we have kind of this
gamified incentive system here where
people can get basically free money for
removing other people's positions in the
protocol so in this situation up here as
the price is going down let's say the
price goes down to $75 backing $50 of
DSC this is way lower than our 50%
threshold so what we're going to do is
we're going to let Liquidator Liquidator
take this $75 backing takes the $75
backing and pays off the $50 DSC and
Burns Burns off the 50 DSC so we're
going to have somebody able to take
their money in exchange for them making
sure our protocol stays collateralized
so that's what this liquida function is
going to do so first off they're going
to be able to choose the
collateral the user that they want to
liquidate and the UN 256 debt to cover
and they'll be able to track the users
and their positions by listening to
these events that we've been emitting
which is exciting we're going to
definitely going to want a lot of n spec
for this app Pam
collateral is going to be the collateral
to liquidate or better yet the
rc20 address collateral address to
liquidate from the user at Pam user the
user who has broken the health Factor
their health Factor should be below Min
Health Factor at Pam
debt to cover is going to be the amount
of DSC you want to burn to improve the
users Health Factor okay we're going to
add a lot of at notices here at
notice you can
partially or liquidate a user just so as
so long as you improve their health
Factor that's all we care about at
notice you will get a liquidation bonus
for taking the users funds right we want
to incentivize them to to actually do
this right if we say okay cool you'll
get the $50 back for paying off the $50
debt it's going to be hard to
incentivize people to do that but if we
say you get all $75 and all you have to
do is pay back 50 then that's going to
be a bonus that they should be able to
take to incentivize them to do this at
notice this function
working assumes the protocol protocol
will be roughly 200 100% over
collateralized in order for this to work
why well because in this scenario if
this drops down to $20 backing $50 of
DSC and if I pay back the 50 DSC and I
get $20 well then I I'm not going to do
that I'm not going to pay $50 to get
back 20 so this whole system only works
if the system is always over
collateralized the only way we can give
liquidation bonuses is if we're over
collateralized so the only way we can
incentivize people to liquidate poor
users is if we are over collateralized
so we could say at notice a known bug
would be if the protocol were
100% or less
collateralized then we wouldn't be able
to
incentivize the
Liquidators and then we would just say
like for example if the price of the
collateral plummeted before anyone could
be liquidated so hopefully this makes a
lot of sense if this doesn't make sense
you know what to do ask chat chbt ask in
the discussions Forum Google it use the
resources that you have to your advance
AG yes yes okay good so debt to cover we
probably want to do more than zero very
lame if they did just zero so we're
going to do more than zero here we're
going to be moving tokens around so
we're going to do non reenen as well
maybe we'll do some more modifiers but
that looks pretty good to me right now
so now what do we need to do well we
need to do a bunch of stuff here well we
first need to check Health factor of the
user right is this user even liquid
datable right remember we want to do
follows
CI checks effects
interactions right we always want to
follow CI so we should do some more
checks here because we should only
liquidate people who are liquidable so
first thing we should do U into 256
starting user Health Factor equals
underscore Health factor of the user
right because we have this health Factor
function which gets that Health Factor
factor and what we can say and let's put
this above now we'll say if the starting
user Health
factor is greater than or equal to the
Min Health factor which what's our Min
Health Factor again one actually it
should be 1 E18 and we're definitely
going to write some tests to make sure
this is correct so 1 E18 because we're
using our Precision here what else are
we doing Health Factor why didn't we
write test elsewhere revert if Health
factor is broken oh I guess we haven't
tested this yet well I guess we're going
to be testing it very soon make sure
that that Health Factor bid is right so
the starting user Health factor is
greater than or equal to the health
Factor then we revert right revert DSC
engine uncore underscore Health Factor
okay right Health factor is fine got a
new error let's go to the top scroll
down error Health Factor o back back
cool back down here remember I'm doing
control minus to go back might be
something else depending on your setup
so let's do some thinking so now we have
their starting Health Factor what do we
want to do we want to burn their DSC
debt right we want to reduce the amount
of DSC they have and take their
collateral we want to remove them from
the system basically right so how do we
do that let's say they have 140
$140 of eth deposited and $100 of DSC
with a setup like this their health
Factor should be below what it currently
is what we could do is we can say okay
we're going to cover so this is what bad
user this dis bad user has that means we
could cover we could say okay we're
going to cover debt to cover is going to
equal that $100 and we need to pay back
$100 so we're going to have to get a u
256 collateral or token amount from debt
covered covered cover covered so we need
to figure out okay if we're covering
$100 of debt $100 of DSC equals how much
eth so we're going to pay back $100 of
debt and how much eth is that okay how
much e eth is that so we're going to get
the token amount of De color the eth
basically equals and we're going to have
to do some pricing stuff we're have to
say it equals get token amount from USD
so we're going to add the collateral and
the debt to cover so we're going to
figure out okay how much of this token
are we going to get right we're going to
cover $100 worth of debt how much in eth
or whatever collateral token is $100
worth of debt so we're going to create a
new function get token amount from USD
this is going to be a public view
function so we're going to scroll all
the way down here public and external
view functions
function get token amount from USD and
we're going to pass an address token or
collateral unit 256 USD amount in way
this will be a public view returns
256 and we're going to have to do some
price feed stuff so we're going to need
what are we going to need to do we're
going to need to get the price of eth or
the token and then we're going to have
to say Okay so if the pricing is dollar
per eth and we have eth how do we get
the dollar well if we do some math here
let's say it's $2,000 of eth and we have
$1,000 right how much eth is that we're
going to do the 1,000 divided 2,000
which is going to equal 0.5 eth right so
we're going to do this number divided by
this number the amount the USD amount in
way divided by the price and that's how
we're going to get get this token or
collateral amount from the USD amount so
we'll say agregator V3 interface price
feed equals aggregator V3 interface of S
price feed of token oh wow it
even build in a lot of this for me
that's great yes this looks right so
we're going to get the price feed of the
token and get called latest round data
on it to get the price here and then
we're just going to do this right here
so we're going to say the amount USD in
way divided by the price or unit 56
price is this the full story no
absolutely not because we should always
multiply first so we're going to do this
times our pre cision then we can divide
by the price but is that the whole story
no because the price has eight decimal
places and we needed to have 18 so we're
going to do additional feed Precision
like this
is Alec right so if we have let's say we
have 10
E18 for Mountain way we're timesing that
by 1
E18 like this we're saying divide that
by the price let's say the price is
$2,000 $2,000
E8 times the 1 E10 the additional piece
here this looks pretty correct do we
need anything else let's see just pull
out the calculator right so 10 1 2 3 4 5
6 7 8 1 2 3 4 5 6 7 8 9 10 * 1 1 2 3 4 5
6 7 8 1 2 3 4 5 6 7 8 9 10 divided by 2
1 2 3 2000 1 2 3 4 5 6 7 8 9 10 1 2 3 4
5 6 7 8 equals this number which is
probably a half right 1 2 1 2 3 4 5 6 7
8 9 10 11 12 13 14 15 that right 1 2 2 3
4 5 6 7 8 9 1 2 3 4 5 6 7 8
9
89 0 point is that right oh because it's
$10 thoughh yes this is right I was like
I thought we did $1,000 but this is $10
yes okay cool that looks good to me
great and then obviously we're going to
test this soon to make sure that is
right so we have this function where we
get the token amount from the USD so
we're saying hey we're going to cover
$100 of your debt or something like that
how much eth is $100 worth of your debt
maybe it's if the price is $2,000 we
obviously we would do 100 divid by 2,000
0.05 right there right this is going to
be something like 0.05 eth or whatever
it is right cool so we have how much eth
we need to take away from their
collateral as a reward for paying back
this DSC but additionally we also want
to
and give them a 10% bonus right because
we want to incentivize them if they're
just if it's just a one for one they're
not going to want to do this so let's
say we're going to give them a 10% bonus
so we are giving the liquid dator
$110 of weth for 100 dsse whatever $110
of we is going to be we should Implement
a feature to liquid dat in the event the
protocol protocol is
insolvent we're not going to add that in
yet though but we should probably add
something like that and then and sweep
extra amounts into a treasury but we're
not going to add either one of those so
don't worry if that's confusing don't
worry about that we're not even going to
implement that anyways so we'll say you
256 bonus colateral equals this token
amount debt covered times some liquid
ation bonus that we haven't defined yet
although we're going to do 10% divided
by 100 but I hate these floating numbers
so we're going to do something better
than 100 we're going to do Liquidator
Precision which is 100 we'll do unit to
256 private
constant liquid dator bonus equals Shan
bonus it's going to be 10 be like this
means a 10% bonus and since it's going
to be 10/ div by 100 that's going to be
10% let's go back down liquidation bonus
divided by
liquidation Precision so the bonus
collateral is going to be 10% so let's
say it was 0.5 0.05 eth we're going to
multiply that by 0.1 right because we're
going to multiply first and then divide
to get
0.05 eth so as a total or that's not
quite right it's going to be 0.5
0.05 time
0.1 equals yes so that means they're
going to get
0.05 five is that right I think that's
right why why am I being bad at math I'm
one yes okay cool great so that's what
they're going to get this bonus
collateral we got to add the bonus
collateral to the actual collateral
we're going to say like you went to 256
total collateral collateral
to redeem equals token amount from
covered plus bonus collateral like that
and so and now we need to redeem this
amount of collateral for whatever's
calling the liquidate function right we
need to redeem that collateral and then
we also need to burn the DSC from this
user as well so we need to give them the
collateral and burn the DSC that they're
covering with their debt to
cover now though if if we look at our
redeem collateral redeem
collateral function which is public
right now we can see as inputs it takes
token collateral address and amount
collateral right and it is hardcoded to
message. Sender our third party user
isn't the one with the bad debt right we
need to redeem a random person's
collateral so what we can do is we can
refactor this code so that there's an
internal redeem collateral function that
can redeem collateral from anybody right
and only very permissioned functions can
call redeem collateral so what we're
going to do is actually we're going to
take all of this code and we're going to
change it and we're going to delete it
and we're going to put it into a
different function all the way down the
in private and internal functions here
we're going to make a function
underscore redeem
collateral and this is going to be an
internal function where we can actually
redeem collateral from anybody and as
input this is going to take address
token collateral address same as the
regular redeem collateral also the unit
256 amount collateral but we're going to
add address from and an address to so
this way and this is going to be private
so this way somebody can liquidate an
address from and then get the rewards to
and this is where I'm going to paste all
that code we got from above and I think
this looks good yeah we're just going to
paste that code exactly like this
instead of doing doing message. sender
this is just going to be from and emit
collateral redeemed we're going to
update our collateral
redeemed to redeemed from we'll do
address redeemed from address redeemed
to we'll do token and then we'll have
amount not be indexed so from to token
and amount so now we got to refactor
some of these collaterals so from when
we do just the regular redeem collateral
we'll update this in a minute actually
yeah we'll update that in a minute so in
this internal function it's going to be
from from to address collateral and then
we're going to transfer we're going to
transfer the tokens to whoever the two
is so this is going to be like the
Liquidator Mount collateral and do that
and now we're going to use this internal
function up in our regular redeem
collateral function that we created
right uh up here oh no up this one up
here we're going to dump all this and
just use this underscore redeem
collateral and for here we're just going
to say message. sender so from message.
sender to message. Sender token
collateral address amount collateral and
now that we have this internal function
we can use this redeem collateral bit
down in our liquidate which is down here
we can now just say redeem collateral
from from the user that's being
liquidated so from user to whoever's
calling liquidate here so it's going to
be message. sender the collateral token
that we're liquidating and then finally
the total collateral to redeem so this
is why I said we were going to do a
little bit of refactoring soon right we
generally only want to redeem collateral
from into the same person however when
we're doing a liquidate we're going to
redeem to whoever's calling the liid dat
so they're going to get that reward the
total collateral to redeem is that total
amount debt to cover plus some bonus
here and now we actually need to yeah we
need to burn the DSC now so right now if
we look up burn burn DSC we have this
public function which just does the same
thing it just does it just burns from
message. sender we're going to have to
do the same here we're going to want to
make an internal burn DSC function that
allows us to burn from anybody right so
if we scroll down to where we're doing
those private functions or scroll up so
we have this internal redeem collateral
we also are going to need an internal
burn DSC function uncore burn DSC unit
256 amount DSC to burn address on be
half of like whose DSC are we burning
for whose debt are we paying down and
then address DSC from where are we
getting the DSC from we a private and we
can go back to the burn DSC this burn
DSC function right and same thing we can
just copy all of this go back to our
internal burn DSC function paste this in
here we're going to want to update this
right instead of message. sender this is
going to be on be half of we're going to
B we're going to take away their debt
we're still going to do this but instead
of message. sender it's going to be ESC
from and it's going to be amount DSC to
burn this is still fine this just needs
to be a amount DSC to burn and we don't
need to check Health Factor yet because
this is going to be our internal
function so we might even say like in
the comments here low level internal
function or at Dev low-level internal
function do not call unless the function
calling
it is checking for health factors being
broken so now we have this burn DSC we
can go back up to our burn DSC function
we can just swap delete all of this call
burn DSC and this going to be message.
sender message. sender amount right
somebody calls burn DSC thems great
we'll just have them call this burn DC
amount to burn on behalf of themselves
from themselves oh looks like got this
backwards amount should be the first one
okay and now we have this burn DSC
function we can go down to look
liquidate and we can call burn DSC what
are the uh amount on behalf of from so
the amount is going to be this debt to
cover amount on behalf of is going to be
the user and the one who's going to be
paying this is going to be message.
sender right because it's the whoever's
the Liquidator whoever's calling
liquidate is going to be the one who's
paying down the debt to cover right
paying back that minted DSC and now
since we're doing these internal calls
that don't have checks we absolutely
need to make sure we're checking this
health factor is okay right so we're
going to do un 256 ending user Health
Factor equals underscore Health factor
of the user and if the ending Health
factor is less than or equal to the
starting Health Factor right if we
didn't improve the health Factor we
should revert DSC engine underscore
Health Factor not improved copy this go
to the top we'll do error DSC engine
Health Factor not improved go back down
so if we don't improve the health Factor
we should 100% revert and then also if
calling this liquidate function if
paying down some debt and doing all this
stuff actually hurts the liquidator's
health Factor we should also revert
right so we should also call revert if
Health factor is broken for the message.
sender right if this process ruined
their health Factor we shouldn't let
them do this okay so we're following
checks effects interactions here the
most part these two functions are making
external calls to external contracts
right and then we're doing kind of like
a check afterwards again this is a bit
of a trade-off we could calculate before
and then run this but that's kind of gas
inefficient we're just going to check
after we do all this hey just make sure
that the health factor is okay make sure
that we didn't break anyone's Health
Factor so now we have this liquidation
function which is incredibly powerful
and kind of what ties this whole thing
together right there's an incentive here
for people to call liquidate so that our
protocol is never insolvent right and I
know I've been kind of throwing around a
lot of these financial terms but our
protocol always has more collateral than
it has minted DSC the value of the
collateral should always be more than
the minted DSC always incredibly
powerful function we're obviously going
to be testing the living hell out of
this right because we want to make sure
it actually
workso okay what do we need now oh looks
like we broke some stuff oh this needs a
semicolon what else do we break there's
a little red thing down here let's go
fix it amount this should be amount DC
to burn got a couple more red things
redeem collateral user message as sender
redeem collateral uh oops we did these
backwards I'm going to copy these
put these at the front okay oh looks
like that fixed pretty much most of this
I don't see any more red stuff on the
side here so we're going to go ahead and
run Forge build make sure everything's
at least
compiling awesome maybe we'll even run
Forge test I don't think this would have
broken any tests looks like those are
working fine but all right we're doing
some fantastic work and like I
said at this point I probably would be
any test with this but we're almost done
with all of our code here so we're just
going to keep going right and this code
is starting to look pretty darn good
right we've got these amazing natspec
comments in here we've got comments all
over the place maybe these probably
should be cleaned up a little bit but
that's fine we've got a little this
little Dev thing a shout out saying hey
don't let anybody call burn DSC without
checking the health Factor right this is
really good to tell Auditors and
Security Professionals about this right
it's really good to call this out we've
now have this internal redeemed
collateral function so it can be used
for the liquidate or the redeem
collateral Health Factor it looks like
pretty much all the functions that we
originally wrote are working in here
right and the reason again that this all
works is because when we mint DSC we can
only mint as much as we have collateral
in the system we're setting this
exchange rate essentially for our
protocol hey cool you have a $100 you
have $150 worth of eth deposited great
you can mint $100 worth of DSC
so it's this exchange rate that we're
setting which is maintaining the price
let's just double check that we have
kind of most of the functions that we
want here and let's also check that
they're in the right place so we go to
the top here got a whole bunch of Errors
that's great got a whole bunch of
constants because we hate magic numbers
we have a mapping of price feeds a
mapping of collateral deposited from
user to token to the amount they have
deposited we have their debt or their
DSC minted collateral tokens the idsc
token we've got some events in here
couple modifiers some
functions we've got a way to deposit
collateral and mint DSC in one
transaction we've got a way to calculate
health Factor I'm going to tell you
right now there's actually a bug in here
ho not going to tell you what that bug
is yet though maybe you can figure it
out we've got some view functions down
below we're probably going to add more
as we write tests but this is looking
pretty darn
good now like I said there's at least
one big bug and there might even be and
there's likely more but there's at least
one big bug in here so that would be a
good time to take a break because after
this we're going to go deep into writing
tests for this protocol we're going to
write some new tests and we're going to
show you some really Advanced testing
methodologies so take that break go for
a walk and I'll see you in a few
all right so let's pull up our terminal
here
Forge
coverage oh we got some work to do all
right well no time like the present
let's get into it so we have some price
feed tests over here we probably also
want to set up some Constructor tests
right we want to make sure that stuff is
being initialized correctly so let's
copy that we'll do
constuctor structor test like that we'll
fix this so our ADHD doesn't go crazy
and we'll do function test something
what are we testing uh let's go down to
the instructor here and what should we
be testing okay well we're doing this
revert here so we should make sure we
actually are reverting correctly when
the lengths aren't the same so function
test reverts if token length doesn't
match price feeds
feeds public zoom in zoom in we're going
to be in here a while so function this
now we're going to do we're going to
create some address arrays address array
public token addresses address
array public price price feed
addresses we're going to say token
addresses. push we're going to push we
into here feed addresses
add. push
FUSD price feed and we're going to push
two in here oh cool BTC do we have I
guess we should pull BTC USD price feed
get this too so FUSD BTC USD price feed
okay oops price feed addresses price
feed addresses so we'll push the two of
those in there now we'll do
vm. expect revert DSC engine dot what's
the name of the error copy
this boom do selector selector like that
doing some toggle word wrap now X spect
reverts now we call a new DSC Engine
with the token addresses and the price
feed addresses oh what else goes in the
DSC oh we also need the address DSC
right address DSC so this should revert
let's see for test
DM nice okay that's passing all right
cool price feed tests we're testing
getting the USD
value I think we had another one down
here right we had something like get
token amount from USD we sure did this
is a public view so let's test this one
as well do function test and I usually
just like to paste the function names
especially when they're like this
public so we're going to do basically
the opposite of this this got the USD
value of some eth amount we're going to
do un 256
USD amount right equals we'll say 100 e
100 ether and then
256
expected we do a little bit of math here
if we have if it's
$2,000 per eth and we have $100 we're
going to do 100 divid 2,000 so 100 divid
2,000 0.05 so we can say expect a we is
0.05 ether now we'll do U into 256
actual we equals dsce do this function
with the lowercase pass in we and then
the USD amount assert equal
expected X expected weth and the actual
we the two of them should be the same
Let's test it out Forge test
DM nice that passed all right let's keep
going POS a collateral test yes test
revert of collateral zero that's good
what else let's go to this deposit
collateral deposit
collateral function okay we should also
revert here we're basically just going
to go through this whole function and
kind of test each line we just tested
this one let's tested this one so this
will be something like function test
reverts with
unapproved collateral
public we'll make an erc2 mock some Rand
token right we'll just make some silly
token new erc2 mock ran will be the name
ran will be the token do we have a user
in here think we do have a user right
okay yeah we do have a user let's give
this money to a user and then we'll give
him some starting amount I think we have
starting erc20 balance or amount
collateral yeah we'll do amount
collateral give them the amount
collateral we'll do
vm. start prank this user who has this
token and they're going to try to
deposit this collateral and we're going
to expect it to revert so vm. expect
revert and this error here is allowed
token right now doesn't take any
parameters so that's easy enough for us
let's just copy this we'll do DSC
engine. this. selector and then we'll do
we'll call the deposit collateral so dc.
deposit collateral address of that Rand
token right Rand token or random token
we'll do amount collateral as well so
it'll just be that whole amount and then
we can do vm. stop prank and that should
work Forge test-
M see if that's correct
yep looking good all right let's keep
going what else what's next deposit
collateral not that one okay did this
did this non- reentrant we could be go
ahead and test re-entrant I'm going to
skip doing that for now but we probably
do want to do some re-entrance the test
at some point but yeah I'm going to skip
them for now and all right cool so then
we can start testing some of this so if
they deposit collateral we should see
that they actually do this we should see
that they emit an event let's go ahead
and do that so let's do function test
canit
collateral leral and get account info
because once they deposit we have this
get account
info oh it's private right now so let's
actually go to the bottom let's create a
public version of that so we'll do
function get account in for
this will be uh external external view
returns return this can't address user
returns these two I'm going to copy this
go back
down uh external view
returns these two things and we're going
to say total DSC minted comma B value in
usdc equals this internal function and
that should be good right oh we're going
to do instead of message. sender we'll
do address user this paste the user in
here so now we can get the total DS
minted and their collateral value in USD
from an external view function we should
be able to get that information so let's
have them actually deposit collateral in
here and since we're going to be doing a
lot of deposited collaterals we know we
can actually make a modifier I'll
deposited collateral
this bm. start prank
userc 20 mock weth in order to deposit
weth we need to approve address
esce comma amount
collateral we're going to do dsce
deposit collateral we amount
collateral. stop prank. stop prank
underscore like this
modifier right and we'll have this can
deposit collateral and get account info
we'll have them deposit collateral and
we'll now get that account information
so we'll say U into
256 total DSC minted
256
later lateral value in USD equals dsce
do get account
information the user oh from the user
and now what we're going to say we're
going to make sure these two numbers are
correct the total DSC minted and the
collateral value in USD they should have
minted no DSC so un 256 expected
expected total DC minted yep equals zero
un 256
expected colateral value in USD equals
this is going to be that dsce doget
token amount from USD with collateral
value in USD so expected collateral
value should just be this function then
we can say assert
equal total DSC minted should be this
expected total DC minted and then we can
do assert equal feral value in USD it's
going to be this expected collateral
value in USD all right clear Forge test
DM all right we have a fail you know
what to do we're going to run it again
Dash VV VV see what we messed up
here says the assertion failed left is
this number and right is a much smaller
number this is why it's not cool to do
two types of asserts but I know it's the
second one so we're saying the clal
value in USD is this but the right side
side is expected collateral value is
going to be this so let's see what's
wrong here 1 2 3 4 5 6 7 8 9 10 1 2 3 4
5 6 7 8 so it's
saying collateral value in USD is 20
grand does that make sense well it's 10
ether time
$2,000 equals 20 grand right so that is
so
the collateral value in USD is right it
looks like my right is wrong
collateral expected collateral value in
USD dc. from token amount we oh what the
heck why am I doing that 1 2 3 4 5 6 7 8
9 10 1 2 3 4 5 6 7 8 so it's saying that
$20,000 is equal to 10 e so I don't know
why I'm calling this oh this is actually
expected expected deposit amount and we
shouldn't be comparing these two to this
should be compared to the
amount collateral right so 10 is how
much collateral we're putting in here
and then collateral value in USD yeah
that's correct so we're getting we're
using this collateral value in USD to
get the expected deposit amount that
looks more correct okay cool Forge test-
M and this is one of the kind of weird
Parts about writing test right okay cool
we fixed it sometimes your test is wrong
like what we just showed here I wrote my
test wrong but sometimes your code is
wrong and that's what these tests really
should be testing hey when is the code
actually wrong making some progress
Forge
coverage not a whole lot of great
coverage here let's keep going so
instead of me just kind of walking you
through the rest of these tests you know
how to write these tests in this file
I'm not really going to show you any
more unique tests right but like I said
there is at least one glaring issue in
our DSC engine. Soul there's a big issue
in here right we definitely need some
more getter functions as well so write
getter functions as you test but there's
at least one giant issue so what I'm
going to do now is instead of me
literally walking you through the rest
of these tests I mean it's you can see
all the tests in here is I'm going to
challenge you to write these tests
yourself to get this for for coverage up
remember you can do Forge coverage to
obviously see what's going on we're
focusing on this one right now you can
also do Forge coverage D- report to see
oh D- report debug to actually see the
exact lines that are missing right and
all these things are just kind of line
items line items whatever IC doesn't
matter scroll scroll scroll scroll
scroll okay these are the ones that are
actually issues right what which one's
this oh that's that is the decentralized
stable coin we want to write test for
that as well but let's just focus on
this massive list up here DSC engine
focus on this list and write some tests
for this because yeah the rest of these
unit tests or staging tests we're not
going to learn anything new this is just
one of these things that you got to do
that you got to get good at that you got
to write now you don't have to get to
100% if you get it to like 85 90 that's
pretty good some of these tests are
actually very difficult to write but you
should writing these tests should find a
glaring bug in at least one of these
functions and maybe if you find more
than one glaring bug that's great make a
PR make an issue to the repo but I want
you to pause the video and take some
time right it might be an hour it might
be two hours it might be 30 minutes
right depends on how quick your AI buddy
is how quick you are and write some more
tests and sometimes actually you can
even come to your contracts
and you can do something like grab this
go over to your chat GTP say hey this is
one of my solidity functions can you
write some tests for it for it in
Foundry and because it doesn't know what
Foundry is it's going to totally bunk it
up but you could use chat PT to help you
write some tests as well help you get
your coverage up but there's definitely
one major issue in here you might even
need to go back and refactor some code
right I'm going to say keep that to a
minimum though but there are some spots
where maybe you need to refactor your
code to make writing tests easier maybe
you need to write some more helper
functions right take this time to
experiment and tinker and think what
should I do how can I make sure my code
is safe and importantly how can I write
enough tests to find this bug that
Patrick is talking about right maybe you
do some refactoring now don't write any
fuzz tests yet we are going to go over
fuzz tests in a little bit but yeah just
try to write the rest of these unit
tests rest of these pseudo integration
tests and I'll see you when you come out
the other side but when you finish
writing the tests take a break take a
minute I want this to sink in this is
one this is the most advanced lesson
that you're going to take in this course
and to be honest probably it's going to
be one of the most advanced courses
you'll ever take in solidity okay so I
want you to take your time with this I
want you to understand what's going on
sometimes it might even make sense for
you to go oh well what is maker Dow do
how can I learn more about stable coins
where else can I go maybe I can ask chat
gbt for questions right those of you who
are going ah I really don't want to do
that you can 100% just copy paste mine
but I recommend you go through this
exercise okay lock off some time and
I'll see you soon
all right welcome back did you find the
bug how your test looking if you run
Forge coverage did it look something at
least like this do they look better do
they look worse so I copy pasted a whole
bunch of my tests and we're looking
pretty good here we definitely should be
increasing the test coverage of our
branches and we can definitely get these
a little bit higher but for at least the
DSC engine
we have a much better code coverage
going on here and I hope your code
coverage looks looks like this too now
additionally I did a little bit of
refactoring writing these codes one of
the main things that I added was I added
this calculate health Factor function
reason I added this calculate health
Factor internal function was so that I
could have this public calculate health
Factor function and then in my test what
I could do is have an expected Health
factor and that way when function breaks
Health factor I can pass it into the
expected Health Factor error revert here
in one of my tests right when we're
testing to expect an event I added this
new function to do that and having a
public function like calculate health
Factor might make it easier for people
to see what their health Factor might be
if they make some change right so that
was one of the big ones I made and the
bug was in the health Factor as well or
at least the bug that I planned to be in
there so in my calculate health factor
which instead of in our underscore
Health Factor function I'm just calling
this calculate health Factor function
right I'm getting the account
information and then just passing it to
this calculate health factor and in this
we needed to add a checker for if the
total DS cemented was zero and if it's
zero then we said okay cool your health
factor is going to be the max U and 256
or something like that right and the
reason that we need this is if someone
deposits a ton of collateral but has no
dsed well their health factor is going
to divide by zero which we can't have so
calculating someone's Health Factor
after they deposit collateral would
result in an issue we'd Break Stuff we
don't want to break stuff and then the
final piece that I did was I added a ton
of external view functions just to make
it easier to read and interact with this
protocol so those are some of the
refactors that I did and then obviously
we added a ton of tests there's nothing
nothing really new in here it's just you
got to write the test right everything
in here you've learned you can do and if
you wrote some tests and you got this to
a high level of coverage around 90% you
should be incredibly proud of yourself
this is hard to write test for this is
very difficult project so you should be
incredibly proud of yourself for just
getting this far but guess what we're
not even done because we want to make
this code so freaking amazing we need to
think a little bit about security and
we're not going to go too deep into
security however we should ask ourselves
some questions right when we're working
with a code base we want to say hm we
should always always ask what are our
invariant /properties what are the
invariance slopies of the system and
that way we can write some stateful and
stateless fuz tests now I know we
briefly went over one form of fuzz
testing but now we're going to go a
little bit deeper and I made a video on
state less and stateful fuzz testing
recently and we're going to go ahead and
watch that so you can have a better
understanding of what stat less and
stateful fuzz testing is and why it's so
important especially for a project like
this that has potentially a lot of money
moving around so let's understand what
fuzz testing is let's watch this video
all right contracts are written and
tested can I ship my code no I can
easily break this with a flash loan
attack a crap I didn't think about that
let me fix all right how about
now if I make a flash loan on a I can
use that loan to lock up a cdb on Faker
Dow and I can exploit the Oracle by
re-entering your dinner reservation at
Chili's causing a bridge malfunction on
the flux capacitor bypassing the
possibility media I can exploit your
contract I exploit your contract
most of the time hacks will come from a
scenario that you didn't think about or
write a test for but what if I told you
that you could write a test that cannot
check for just one scenario but every
scenario let's get froggy fuzz testing
or fuzzing is when you supply random
data to your system in an attempt to
break it so if this balloon is our
system/ code it's us doing random stuff
and attempt to break it this is chain
link now why would we want to do all
that let's say we have this function
called do stuff it takes an integer as
an input parameter and we know that no
matter what we give it as an input our
variable should always be zero should
always be zero the fact that this
variable should always be zero is known
as our invariant or our property of the
system that should always hold in our
balloon example if we Market our balloon
as indestructible or unbreakable or
unpoppable the invariant that would hold
would this balloon cannot be broken and
unlike this balloon in real life we can
write a test that will call the do stuff
function many times with random data and
check to see that our should always be
zero variable is always zero now a
normal unit test for our code might look
like this we pass a single data point we
call the function and then we do our
assertion to make sure that should
always be zero is in fact zero and with
this we might think our code is covered
but if we look back at our do stuff
function a little bit closer we can
clearly see that if our data input is
two should always be zero will end up
being one this would break our invariant
should always be zero will not be zero
now this may seem obvious for this
function but sometimes you'll have a
function that looks like this
[Music]
it would be insane to write a test case
for every single possible integer or
scenario so we need a programmatic way
to find this scenario now in our code we
also see a second exploit but we'll get
to that in a minute now there are two
popular methodologies to find these Ed
cases fuzz test/ invariant tests and
symbolic execution SL formal
verification we'll save the ladder for
another video if we were writing our
code in Foundry this would be our unit
test writing a fuzz test in Foundry
where we do all this random inputting is
going to be really similar instead of us
manually selecting our data right in our
test parameter we'll add our variable
comment out this line and that's it now
when we run a Foundry test here Foundry
will automatically randomized data run
through a code with a ton of different
examples this is as if they run with
data equals 0 data equals 1 data equals
this number that's a t but whatever you
get the picture now if I run my unit
test you'll see that the unit test
actually passes however if we run this
fuzz test you'll see it actually gives
us an output where it says assertion
violated counter example gives us the
call data and the arguments it was able
to find out by randomly throwing data at
our function call that two breaks our
invariant AKA it makes a such that
should always be zero is not zero now
it's really doing semi- random data
instead of purely random data and the
way your fuzzer picks the random data
matters it won't be able to go over
every single possible un 256 so
understanding how your fuzzers pick the
random data is an advanced thing that
you should learn later on at the moment
I think the trailer bits a kidna SL
optic integration is probably the best
fuzzer out there and it easily has the
best logo of all time but ripped Jesus
is a solid second so now that we have
our counter example here we can use this
to go back into our contract find out ah
okay so we are doing this wrong delete
this line and then run our test again
and see that it does indeed pass what's
important is this number down here the
number of runs so this did 256 different
random inputs to make our test run in
Foundry you can change the number of
runs in your Foundry Tomo file by just
adding a section like this rerunning
your test and now you'll see it did a
thousand different examples the number
number of runs is really important
obviously because more runs is more
random inputs more use cases more chance
that you'll actually catch the issue and
now congrats that's the basic of fuss
testing let's just do a little recap
here before going further the first
thing you need to do is understand our
invariant or property of the system that
must always hold and our example should
always be zero was our invariant
understand your invariant and then write
a test that would input random data to
try to break that invariant now if we go
back to our example contract though
you'll see with our fuzz test we were
able to find this first use case case
however it didn't find this second
scenario where should always be zero was
set to one if hidden value was seven in
order for this to revert hidden value
would need to be seven and the only way
to set hidden value to seven would be to
First Call do stuff with seven which
would set hidden value down here and
then call do stuff again with anything
our fuzz test as written would never be
able to find this that's because this
fuzz test is known as a stateless fuzz
test which is where the state of the
previous run is discarded for the next
run if we go back to our balloon example
stateless fuzzy would be doing something
to the balloon for one fuzz run then
discarding that balloon and blowing up a
new balloon for each fuzz run however
instead of doing State less fuzzing we
could do state full fuzzing stateful
fuzzing is where the ending state of our
previous fuzz run is the starting state
of the next fuzz run or example instead
of blowing up a new balloon for each one
of these runs we just use the same
balloon to do multiple random things to
it combined is considered one fuzz run
so a single fuzz run on a stateless fuzz
run would be having data be7 calling do
stuff just using the same contract that
we just called do stuff on and then call
another function on it if this was a
unit test we had we would of course see
this get violated but as you can see
with sufficiently complicated code
coming with these very specific
scenarios are going to be missed to
write a stateful fuzz test in Foundry
you need to use the invariant keyword
and it requires a little bit of setup
and don't get too confused by the
invariant keyword here yes it's being a
little overloaded right invariant test
in Foundry we first need to import this
STD invariant contract and inherit it in
our test contract then we need to tell
Foundry which contract to call random
functions on since we only have one
contract with one function we're going
to tell Foundry that my contract should
be called and it's allowed to call any
of the functions in my contract so we'd
say hey the Target contract for you is
going to be the address of example
contract Foundry is smart enough to know
okay it's going to grab any and all of
the functions from my contract and call
them in random orders with random data
so it's going to call do stuff with
random data and then it's going to call
do stuff with random data and then it's
going to call do stuff with random data
since do stuff is the only function fun
now we can write our invariant by saying
function invariant test always is zero
public and we can just add our resert
cert our example contract that should
always be zero is zero so it'll run do
stuff with some random data if it
happens across seven it'll set hidden
value to7 and then it'll call do stuff
again with hidden values starting at
seven which will trigger this
conditional so now if we run this test
we can see it does indeed find a
sequence where our invariant or our
assertion or our property is broken we
can see first on my contract it's going
to call do stuff with an argument of
seven and then it's going to call my
contract with an argument of some random
number because it doesn't matter what
the input is after it sets it to seven
so now that we have that we can go back
to our code remove this come back to our
test rerun our test and we'll find that
our code is now safe and sound because
our invariants hold up now an important
aside on the term invariant founder uses
the term invari variant to describe this
stateful fuzzing stat less fuzzing is
when you give random data to an input to
a function to see if it breaks some
invariant State full fuzzing is when you
give random data and random function
calls to a system to see if it breaks
some invariant in Foundry fuzzing is
stateless fuzzing and invariance are
stateful fuzzing so when people are
talking about invariance in Foundry
they're usually talking about stateful
fuzzing if they talk about fuzzing in
Foundry they're talking about State less
fuzzing even though they're both
technically fuzzing there's an issue on
the repo to potentially change the name
but I digress so in a real smart
contract your invariant won't be that a
balloon shouldn't pop or some function
should always be zero it might be
something like new tokens minted is less
than the inflation rate there should
only be one winner in a random Lottery
someone shouldn't be able to take more
money out of the protocol than they' put
in and let me tell you what at this
point congratulations you've learn the
basics of fuzzing this is something that
even some of the top protocols in this
space don't use and this is something
that we in cphon use to find High
severity vulnerabilities in smart
contracts hey I'm Alex ran co-founder at
cyphon we use invariant tests during our
audits to identify vulnerabilities that
are often difficult to catch purely with
manual reviews that's not to say they're
a silver bullet they are in no way a
replacement for experts manual review
but they certainly can Aid in the audit
process this needs to be the new floor
for security in web 3 if you're working
with a protocol that isn't doing
stateful fuzzing or invariant or fuzz
test red flag get them to use it make a
PR number one understand what the and
variants are number two write functions
that can execute them do not go to audit
without these don't let your Auditors
let you get away with not having them so
this video was just to give you the
basics and if you want to learn the
advanced fuzzing strategies on how to
fuzz look Pro be sure to watch our next
video on the topic is that'll give you
the keys to write professional fuzz and
professional invariant tests come on
gang let's make web three better and
I'll see you next time
[Music]
all right so now we've learned a little
bit about invariant tests or fuzzing
tests and why they are so absolutely
crucial especially for a project like
this so we're going to write some
stateful fuzz tests or invariant tests
in foundaries so we can have some more
confidence that our code actually does
what we want it to do and the method
that we saw in that video that we just
watched is kind of the most basic
methodology out there and if we go to
The Foundry docs we can go on the left
side go all the way down to fuzz testing
or excuse me go all the way down to this
invariant testing which invariant
testing like I said is stateful fuzz
testing and we can read more about some
of the more advanced ways to do these
fuzz tests or these invariant tests
we're still going to do the target
contracts but what we saw in the video
was a type of open testing right where
we just have function in variant a and
then the assert and what this does is it
just calls calls all the functions on
this contract to try to break that
invariant now this is good this is great
for an initial run of the code however
we want to do for more advanced systems
like ours Handler based testing
sufficiently complex Protocols are going
to have so many different random
intricacies that we want to narrow down
the random call so that we can have a
higher likelihood of getting and
catching actual errors so we're going to
do this Handler based type of testing in
this example here in this open testing
it just calls any of the functions in
the contract in any order and Handler B
based testing if we scroll down we can
kind of see this example here where we
create a contract called Handler where
we only call functions in specific ways
for example when depositing tokens we
need to make sure an approve happens
beforehand if you just call deposit
without approving that token that's kind
of a wasted fuzz run and if we only have
200 fuzz runs and we're wasting them on
failed fuzz runs well we're the chance
of us actually finding a bug becomes
smaller so if you think of the open fuzz
testing like this where you have Foundry
you call a whole bunch of functions on
the protocol with the asserts Handler is
going to call functions in specific ways
to the functions so that we have a
higher likelihood of calling functions
in orders that we want so we're going to
learn about this Handler based
methodology and we're going to build an
incredibly verbose Buzz testing or
invariant testing setup now
in our founder. toal to work with these
fuzz or these invariant tests we can do
this in variant section here we can say
the number of runs we'll say is 128 we
can also say the depth which is the
number of calls in a single run which we
might do 128 and then one of the most
important keywords you're going to run
into is this fail on revert so let's
talk about this fail on revert keyword
and setting so to create some invariant
tests let's create a new folder or Buzz
test called or invariant or whatever you
want to call it and in here we're going
to need to actually create two different
files we're going to do invariance
invariance test. T.O and then handler.
T.O this invariance file is going to
have have our invariance AKA our
properties of the system that should
always hold right that we just learned
for that video and this Handler this
Handler is going to narrow down the way
that we call functions this way we don't
waste runs like I was saying again if we
call deposit collateral in our stable
coin without approving that stable coin
that's kind of a wasted run and we don't
want to waste runs so this Handler is
going to set our code up set our
contracts up so that we don't waste
these runs and we're going to come back
to this fail and revert in a second for
now let's actually set this to false and
we'll set it to True soon but so the
first thing we always want to do when
writing in variant test when working
with this just want to ask the question
what are our invariant what are the
properties of the system that should
always hold well we can think of some
right well one the total supply of
DSC should be less than the total value
of Catal follow the word wrap which the
total supply of DC which is essentially
the debt should always be less than the
total value of collateral great we have
an invariant that we can test and we
should throw a ton of random function
calls to try to break this one okay what
else what other invariants should we
have maybe our getter functions our
getter view
functions should never revert and this
is actually sort of an
evergreen invariant most protocols can
and should probably just have an
invariant that looks like this getter
view function should never revert now we
can probably think of more but because
because doing these invariant tests can
be a little bit time intensive we're
just going to focus on these two for now
these are going to be the two invariants
that we focus on we try to work with so
let's begin working and writing our
invariant tests and then we're also
going to write our Handler to help make
sure all the function calls that we're
working with actually do what we want
them to do okay let's do it so this is
going to be another test file so spdx
license identifier MIT pragma so oh oh
that's nice that kind of just
automatically added it for me let's put
this to the top
0.
8.18 a little carrot here contract
invariance test like this and to do this
we're going to say we're going have to
import some stuff we're going to import
test from Forge TD test. soul and import
STD in variant from Forge STD STD
invariant Soul contract invariant test
and then we're going to say this is STD
invariant and it's test so this STD
invariant contract if we click into it
it has all this stuff that we're going
to need to work with the invariant one
of the most important functions that it
gives us is this Target contract where
it says hey this is the contract I want
you to call all these random functions
on okay great now just like our normal
tests we're going to have a function
setup external like this and we're going
to set up some stuff right a lot of this
is going to look similar to our unit
test so we're going to have to import
Ploy
DSC
from do do do do script deoy DSC
dos. import DS C engine from do
slsrc SL
DC engine. Soul like this and we'll say
deoy DSC deployer
deployer deployer equals new deoy DSC
and then of course DC engine dsce dce
equals deployer do run and this actually
returns a whole bunch of stuff Central
stable coin dce and helper config so
this is going to be we also need a DSC
so we're going to import that import the
centralized stable coin from dot do do
do SRC decentralized stable coin. Soul
so we're going to do decentralized
stablecoin
DSC and let's get the helper config as
well import helper config config from
do do script slh helper config dos. Soul
helper config config right and so this
turns the DSC dsce and config equals
deployer run great and now if we were
doing this open testing methodology
right if we go back to the docs here
we're doing this open testing
methodology we could kind of finish this
right now what we would do we we would
say Target contract address dce like
this paste this in here and then just by
adding this we're telling Foundry hey go
ahead go wild on this right go
absolutely Wild on this and actually
let's even rename this call this open
invariant test. T.O open invariant test.
T.O we'll say absolutely Go Buck Wild on
this and now we can add our invariant
right we'll say function invariant
variant underscore protocol must must
have more value than total Supply and
this will be a view function and what we
can just say is we want to get the value
of the of all the collateral in the
protocol compare it to all the debt or
the DSC so we can do that pretty easily
by using the collaterals itself right
using our helper config we'll say helper
config do activ Network config we can
get the what does this one do again
let's open up the helber config we can
get these two tokens and just say okay
well what's the balance of these two
tokens in our DSC engine and then what's
their value right so we'll just get
those two tokens we're going to do
nothing nothing we Bitcoin nothing so
we'll do blank blank we BTC nothing and
then those are ERC 20s so we'll import
import I
erc20
from all right cool that looks
good so we'll say erc20 we ic20 BTC this
is wrapped BTC wrapped BTC oops sorry
this is config Active network config we
need to wrap this erc20
20 like this what am I messing up oh
let's do do address we then address we
address BTC like this all right cool so
we're getting we and Bitcoin EXC me
wrapped eth and wrapped Bitcoin and now
what we can do is first we'll say unit
256 total Supply equals DSC do total
Supply so this is the total supply of
all DSC in the entire world right and we
know the only way to mint DSC is through
the DSC engine so through people
depositing and withdrawing collateral
now what we can do is we can say okay
let's get the uint
256 total total we
deposited equals and this is where we
can do
ic20 with balance of address DSC so this
is going to be the total amount of we
deposited into that contract or just the
total amount of we sent to that contract
then we're going to say un 256 total BTC
deposited equals ic20 wrapped BTC dot
balance of address DSC so we have the
total we total Bitcoin now we can get
those values we can say un 256 we value
equals we have a function here called
get USD value where we can get the USD
value of any token of any amount so
we're just going to use that
we value is going to be dsce doget USD
value we and the total we deposited un
256 wrapped BTC value same thing get USD
value WRA BTC total BTC deposited and
now what we can do is we can
do assert the we Value Plus the wrapped
Bitcoin value is greater than the total
Supply and this
is all we would need to do for this open
testing this open invariance that's it
we're done boom and this is why this is
the easiest type of invariant test but
you'll see running this we won't get
great results so let's do Forge testm
and now what this is going to do is it's
going to call all types of functions on
our dsce and try to break this invariant
so it looks like it was able to break
this really easily so let's clear this
and we'll add our Dash BV
BV we'll see what's
up yep yep assertion violated we're
having a hard time actually seeing the
numbers so we're going to import console
in here test
console and we're just going to say
console. log we value so me call
W value or that's w w BTC we we W BTC
value
console.log total Supply total Supply
let's run this again now what do we
get well we got our first issue is that
these are all zeros right and if they're
all zeros then this doesn't hold so we
can do greater than or equal to this is
kind of a bit of a copout right because
if they're equal that makes us nervous
but it's fine right we should always at
least have more collateral in the system
than total Supply so now let's try again
run this test again run successful and
we get hey you pass there's no way for
us to make it such that the total Supply
is lower so this is awesome right well
it's not that awesome we didn't find any
issues we're looking to find issues so
maybe we need to bump up the number of
runs may we need to bump it up to
2000 and let's run this again now you're
going to see this is going to take a lot
longer CU before I was doing 100 runs at
128 depth now it's was doing a th000
runs and each one of these runs has 128
so it took a lot longer and you can see
that it took 14 seconds as opposed to
what it do before with 128 we run
that it did it in 1 and half seconds but
if we look up here on this line this
line is incredibly important we have
calls and we have reverts so this made
16,384 calls and reverted
16,384 times so basically it wasn't even
able to do anything right so what if we
bump this up to a th000 clear run this
we're going to have to wait a few more
seconds
again
oh so it did this many calls and it also
reverted this many times and the reason
that this is still saying pass though is
because we have this fail on revert
equals false and this fail on revert
equals false has some pros and it has
some cons the pro a fail on reverse
equals false is that we can very quickly
write open testing functions like this
and we can very quickly write minimal
Handler functions that aren't perfect
but the downside is it's hard for for us
to make sure that all the calls we're
making actually make sense right because
this could be calling on our on our
engine maybe it's just trying to to
deposit collateral but it keeps using
random collateral out addresses that
don't make any sense so maybe it's
calling this
128,000 times with 128 different
collateral addresses but only two work
right so doing like this is cool for
kind of some sanity check and maybe
it'll catch something but seems like
it's not actually catch anything right
and that's not a very good use of this
so revert on false is fantastic for
quick tests and often if I'm doing a
competitive audit which you can learn
more about in the security course coming
out soon hopefully if I'm doing
competitive audit a lot of times I will
have revert on false be false just so I
can write up inv variant test quickly I
will also write handlers as well like
mini handlers just so I don't have to
get every nook and cranny but I still
will write a Handler so I can narrow
down some of the functions but this open
inv variance seems to have this major
flaw where it's probably making a bunch
of silly calls so this is great for very
small contracts but the more complex you
get like our system here this open inv
variant system it probably doesn't make
sense for us to do because it's not
going to catch anything it's just going
to keep breaking now if we set this to
true we open this back up we run this
again you'll see
exactly one of the calls it makes that
breaks so it looks like it called redeem
collateral for you for DSC this is the
first function call it made which
obviously doesn't make any sense because
you can't redeem any collateral unless
you have deposited collateral and and
you can see the args it put in it put in
some random address some random amount a
random amount and then another random
number right and you'll see we can call
this many times and it'll keep giving us
different that it ran into issues when
we say revert on false is true this can
give us some peace of mind knowing that
if this test passes that means all of
the transactions that went through
actually went through and it didn't make
a bunch of really dumb calls here right
so here fail on revert was false we
called liquidate first which obviously
doesn't make any sense with this with
some horrible random address some
horrible random address some random
amount right none of these make any
sense we call it again we failed again
it called deposit collateral mint DSC
which is good all right so we're trying
to deposit collateral but it used some
random address that isn't approved some
horrible amounts here and it just keeps
failing right so we want to try to
prevent and you can see here in the call
summary it made one run one call and
that call reverted right so we want to
narrow this down to say to try to point
our fuzz our random runs in a direction
that makes a lot more sense right so
this is cool not great opening variance
I'm just now going to comment out this
whole page because we're not going to
use this anymore okay we're I'm going to
leave the file in here though we're
going to create a new file though we're
going to call this
invariance t. soul I am going to copy
this whole invariance thing paste it in
here and uncommon it and we're going to
level this up so that this invariance
file actually let's just call this
invariance so that this new one is using
this Handler method it's using this
Handler method to narrow down the
function
calls and we'll do a mix of setting this
to true and false and you'll see where
some of the advantages and disadvantages
are so we have our invariance file we
have our Target contract here but we
want to make sure we call this in a
sensical order for example hey don't
call redeem collateral unless there is
collateral to redeem
right maybe we want to set this up so
we're going to create a Handler which is
going to handle the way we actually make
calls to the dce so it's basically going
to instead of us just randomly calling
redeem collateral we're only going to be
able to call redeem collateral if there
is collateral to redeem right because
otherwise the transaction is just going
to revert and that's a waste of function
call so now we're going to create this
Handler and instead of our Target
contract being the dsce our Target
contract is going to be this Handler
which handles the way we make those
calls okay so we're going to do spdx
license identifier MIT as you already
know fragma Z solidity
0.88 a little carrot contract Handler
like this and then we're going to say
this contract Handler is going to be
test as well and this is definitely some
Advanced code here so don't get too
discouraged if it doesn't make sense or
if it's hard the first time okay so
we're going to inut report test from
forg STD test. Soul remember to ask
questions and use the forms so what's
one of the first things you want to do
hey don't call redeem collateral unless
there is even collateral to redeem right
we want to make sure that that's this is
a valid run he only call redeem
collateral when there is collateral in
there so this function this contract is
going to do that for us we do need to
make a Constructor though so that this
Handler contract knows what the DSC
engine even is right because it's going
to be the one making the calls to it so
we do need to import the DSC engine from
do do
slsrc DSC engine. we also need to import
the decentralized stable coin from the
decentralized stable coin. Soul
Constructor and these are going to be
the main functions these are going to be
the main contracts that our Handler is
going to call so we're going to say DSN
and dce
and then we're going to say
decentralized stable coin DSC and in the
Constructor here we're going to say DSC
engine uncore DSC
engine decentralized stable coin _ DSC
and then we're just going to say oh cool
I already added it dsce DSC engine and
DSC isore DSC so because these are the
contracts that we want the Handler to
handle making the calls to Great makes
sense so let's talk about this redeemed
collateral right let's just focus on on
making this not revert so we're going to
say okay call this when you have
collateral so the first thing we
probably need to do is what probably
deposit collateral right so we'll create
a function called
deposit
collateral and this function is going to
look a little different than the deposit
collateral in the DSC engine right if
we're look in here this is what it does
we're going to set this deposit
collateral function up in our Handler so
that this transaction always goes
through right it doesn't revert but we
do want to keep the randomization right
we wanted to deposit random collaterals
that are valid collaterals so what we
can do is we can create a unit 256
collateral seed and a un 256 amount
collateral collateral and this is
actually really similar to the fuzz
tests so in your handlers whatever
parameters you have are going to be
randomized so we're going to pick a
random one of the valid collaterals to
deposit and we're going to pick a random
amount of collateral now I'm going to
write this function without any guard
rails and it's going to break and that's
okay we're going to fix it as we go
along but if we were to just not have
any guard rails on this at all we would
just say dsce do deposit
collateral
ceral and we would do you know
collateral and an amount collat Al we
could actually just have this be
address collateral and amount collateral
and we'd say deposit this collateral and
amount collateral and this of course is
probably going to break a lot right
because the collateral we're going to
pass is going to be wrong this is a
random address there are so many
addresses and the amount of collateral
could also break because deposit
collateral reverts on zero right but I
do want to show you what we're actually
going to do in our actual contract here
instead of having our Target contract be
the dsce what we're going to do is we're
going to say Handler is we're going to
import this Handler so we're going to do
import Handler
from handler. T.O Handler Handler we're
going to say Handler equals new
Handler dsce and DSC and now we're going
to say our Target contract is just the
Handler okay excuse me the address
Handler now with this the Handler only
has this one function so we're going to
call deposit collateral through the
Handler which is going to call our
engine and since this is the only
function for it to call this is all
we're going to see if we see our
invariance break so if I do Forge test
DM paste this in we're going to see it
break almost
instantly with an issue but it's only
going to be deposit collateral because
it's the only function that we have
right we run it again it's still going
to be deposit collateral cuz the only
function the Handler has yes makes sense
okay cool hopefully it makes sense in
our Foundry doomo we could say fail on
revert is false we could run this again
let's make this a little bit lower 128
and let's see if we get any valid runs
right let's see if we pick miraculously
a valid address and we get success but
oh it looks like we did do some valid
ones but we've got a ton of reverts here
we've got so many reverts here that
almost half of these runs almost half of
these calls were bad that's not a great
use of our and this isn't really super
helpful because of course of course our
invariant here is going to hold because
the only thing we've allowed our system
to do is deposit collateral right
ridiculous so if we turn this back to
true we run this again we're a course
going to break now because some of these
deposits are going to fail right so it
looks like if we call deposit collateral
with some horrible address
it fails right so that's one of the
first things we want to to have our
handler do we want to say hey you're
only allowed to deposit valid collateral
so what we can do in our Handler is we
can set that up hey you're only allowed
to deposit valid collateral instead of
passing any address as collateral we'll
say U 256
collateral seed okay and what we're
going to do with this seed is we're
going to have it pick from our two coll
lals we're going to have it randomly
pick either we or BTC so we're actually
going to create a function helper
functions we're going to say function
underscore get collateral collat from
seed un 256 collateral seed and we're
going to make this a private view
function it's going to return an ER an I
erc20 actually we're going to do an
erc20 Mock and I'll explain why in a bit
we got to import that import rc20 mock
from at open Zeppelin contracts mocks
slash what is it mock ERC
noc2
mo. cool and so what we're going to do
instead of this line is we're going to
use this line this function to do it
we're going to say if collateral collat
all seed modulo 2 cuz we're only we only
have two collaterals equals equals zero
then return we we can get we by sticking
at the top we'll say erc20 mock we ERC
20 mock WFT
Bitcoin and right in our Constructor we
can get all of our collateral tokens the
I made a function way down at the bottom
here called get collateral tokens which
returns the full array of collateral
tokens if you don't have this feel free
to pause and implement this what we can
do we can say address array memory
collateral collateral tokens equals dsce
doget collateral tokens like this and we
can say we is zero and R BTC is one
that's how we can get those collateral
tokens so we R BTC if collateral C
divided by 2 is z return We otherwise
return wrapped BTC so now we have a
function where we can only get a
valid collateral type so instead of just
depositing any collateral type we can
say uh erc20 mock colateral equals get
collateral from seed collateral seed and
now we're still depositing a random
collateral but this is oh it's got to be
address but this is a valid collateral
address so we're probably more likely to
actually pass a transaction that will
actually go through making us have more
solid more good random calls so let's
try to run this function
now great successful but we ran into an
error right let's see what the error is
so deposited collateral you U and 256 U
and
256 with this huge collateral
seed uh and
nothing looks like we failed here it's
clear let's rerun it with- vvvv so we
can see a little bit output of why we
actually failed okay we ran we still the
only function we're calling is deposit
collateral looks like this failed again
we called deposit collateral with some
weird args we can scroll up and see
exactly why we failed oh we ran to this
DSC engine needs more than zero so it
looks like we tried to deposit zero
collateral Mount collateral was Zero yep
okay yep so we tried to call was zero so
amount collateral was zero so we know
that this is going to fail so how can we
make it so that this doesn't fail or
maybe you're like hey like sometimes it
will be zero like whatever I I just want
a sanity check you can make fail and
revert false right and we can run this
again and now we'll see how often this
actually fails hopefully we cut down on
the amount of times it failed oops let's
remove those
V's great it actually it does look like
we cut down on the amount of reverts we
got not a lot but we we did cut down the
amount of reverts at least by adding
this bounding of the collateral types
but let's keep cutting down on these
reverts and potentially even have revert
on false be true right you're not always
going to have revert on false be able to
be true and sometimes it's quicker just
to have it false and write all your
invariance and stuff but if you want
kind of but is good to aim for this now
the downside of always aiming for this
is that if you make your handle too
specific maybe you'll actually narrow it
down and remove edge cases that would
break the system that are valid right so
it's kind of this balancing game you
have to play with these fuzzing tests
and whether fail and revert to be true
or false there's definitely a little bit
of an art to this so the more you do it
the better you'll get but in any case
collateral seed amount collateral we
need to now change it so that this
amount collateral is bounded between one
and some Max number right so we don't
want this to be able to be zero so what
we can do then instead is we can
actually we actually can bound this so
that in the same way we got a valid
collateral let's get a valid amount
collateral so we can say amount
collateral equals bound this is a
function that actually comes with STD
utils and it bounds the result to an
amount so we want to say okay this
amount collateral we're going to bound
our amount collateral to being between
one we don't want to be zero and then
some like really really big number what
I like to do is up here in the state
variables I'll do U 256 Max deposit size
equals and I'll do type uint
96.
Max and this allows us to get the max
uent 96 value why are we not doing the
max U 256 well if we do the max U 256
and we try to deposit more collateral
later if you do the max6 plus one you'll
get a revert so this is going to give us
a really really really big number but
we're at least not going to hit the
absolute top of amount of deposits we
can deposit so we're going to say we're
going to bound this amount collateral
between one and Max deposit size Okay
cool so let's put this back to false
let's run this test again and let's see
if we cut down on the reverse some more
aha a couple more right only a few
hundred but we did cut down on the
amount of reverts why because we're
passing valid well okay that one didn't
go so well but we are cutting down on
the amount of reverts piece by piece
we're able to bound the collateral but
there's more if we do failon revert back
to true we can see exactly why it's
failing right we'll do- vvv VV we can
see an examp example that is indeed
failing and who these are getting more
intricate right before it was just a
single call was breaking it oh okay a
single call still is breaking it but
they are getting a little bit more
intricate so looks like this one failed
because what we called deposit
collateral we scroll up we're getting
this insufficient allowance okay so
that's why we actually weren't cutting
down on the reverts at all because we're
getting insufficient allowance of course
we need to approve the protocol to
deposit this collateral so of course
this is breaking this is always going to
break so let's do a little prank so
we'll do vm. start prank we'll just do
message. sender and we H will allow this
message sender to Mint some of this
collateral so that they can actually
deposit it right so we can set this up
so that whoever's calling deposit
collateral actually has the collateral
and actually will approve to deposit the
collateral so and this is why I'm using
this erc20 mock so that we can actually
mint some of this collateral so we'll do
collateral. mint message. sender amount
collateral we'll do collateral
collateral. approve address
dsce for the amount collateral then we
can deposit it and then we'll do vm.
stop prank okay now let's clear this
let's have revert on false to be false
and let's see now if we cut down on the
amount of reverts that we get
oh and I added too many V's let's get
rid of those let's run this
again and yep there will be some brief
delays in here of course whoa we cut the
amount of reverts down to zero now all
of our function calls are passing which
means every single run was a valid run
meaning we're using our runs much more
wisely we're not wasting runs on failed
reverts so now I can even set this to
true run this again and we'll see this
passes and none of our runs failed right
so this means what does this mean from a
security standpoint it means that no
matter how often we call deposit
collateral no matter how much we deposit
our collateral we will never make this
invariant false which isn't saying too
much because of course we're not even
the total Supply is always zero of
course holds so this makes a lot of
sense so let's actually keep writing
more functions to do more with the
system but set them up so that whenever
we call them they're always going to be
valid calls
okay so now we're talking about redeem
collateral right okay cool so now
there's actually now we actually have a
function a valid function to deposit
collateral now let's actually have a
valid function to redeem collateral so
we're going to do the same thing
function
redeem
collateral
collateral we're going to do the same
thing it's going to take a un26
collateral seed for which collateral to
redeem un 256 amount
collateral so be a public and we're
going to do something very similar here
we're going to say we're only going to
choose a valid collateral by saying
erc20 mock collateral equals underscore
get collateral from seed collateral seed
and now we should only allow people to
redeem the maximum amount they have in
the system right so we're going to say
un 256 Max collateral collateral to
redeem equals DSC engine
engine dot get
total get
collateral balance of user do we have
this
function DSC
engine so this is one that I added in
it's going to get the collateral balance
of a user if you passing the user in the
token so if you want to pause and add
this in feel free to do so get
collateral balance of user where we add
the address of the
collateral and the message. sender so
it's going to get the total balance of a
user oh and this should be
dsce and then we're going to bounce the
amount collateral to this max amount
that they have to make these always
valid they should only be redeeming as
much as they put in the system amount
collateral equals
bound amount
collateral uh we're going to say redeem
between one and the max collateral to
redeem because we don't want them to
redeem zero of course and then we're
going to say it's going to be public
then we're going to say dsce do redeem
collateral address collateral and amount
collateral right so now we have two
functions to randomly call so we're
going to be depositing collateral and
redeeming collateral let's run this
invariant it only has two functions it
could call go to the found. toml fail
and revert is true oh and it looks like
we found an edge case so now we can read
this and see what it's doing so it's
depositing some collateral 1 38 1 and 82
and then it's Redeeming the collateral H
not actually sure what's going on so
let's add
this Dash
vvvv to see more into the actual
transaction that's failing oh my
goodness we got an even bigger one here
so what are we saying oh Max is less
than the min oh looks like I'm messing
up with my bounding so it's not even
really an issue so if we go back to the
Handler bound M collateral One Max
collateral Al to redeem ah okay so if
Max collateral to redeem is zero this
will break so we actually do need to
keep zero in here and then we can just
say if you know amount collateral equals
equals z we could say if it's zero
return or what we could do is we could
use this keyword called assume like vm.
assume which will say if the Boolean
expression evaluates to false the fuzzer
will discard the current fuzz inputs and
start a new fuzz run so we're going to
do if the amount collateral zero just
return don't call this function right
cuz this is going to fail so let's go
ahead and run this again now compiler
run
successful well would you look at that
we are now passing again so people can
if we go back to the Handler people can
now redeem collateral and all of these
redeeming are going to be valid and all
these deposits are going to be valid
right they can only redeem
valid Redemption amounts now here's
where this fail and revert equals true
is can be a little bit deceptive let's
say right now we're only letting you
redeem the max collateral to redeem
let's say there was a bug where a user
can redeem more than they have this fuzz
test wouldn't catch this it's because we
have this fail and revert is true if
this fail on revert was false and we
didn't have this line right and we just
said this is the max deposit size this
is a test where we might actually catch
that bug right and if we run this now oh
and with fail on revert is false we are
going to get a whole bunch of reverts we
are going to get bad transactions
however and you can see we can see the
number reverts over here and it actually
is way less than it was before which is
awesome and if we run it again still way
less than 8,000 which is what it was
before but if we have fail and revert
false we can actually catch this so The
Foundry team is actually working on
allowing some test to be fail in revert
and some test to be not fail in revert
so you can kind of pick which one for
which functions instead of kind of
having to blanket everything so just
keep in mind the dangers here okay of
always defaulting to fail on revert
equals true okay they have their
trade-offs and if you do fail on rever
equals false you can sometimes write
these handlers a lot quicker
but okay so we have some stuff here we
can deposit collateral we can redeem
collateral what else so we probably
should figure out a way to get some
total Supply right so what we're going
to do is now we're going to finally make
our mint function function mint
DSC public like like this and what do we
want to put in here well if we go to the
DSC engine go to Mint DSC go to this
mint DSC function it takes an amount of
DSC to Mint so we'll do the same thing
we'll say uint 256 amount and that's it
we'll mint a random amount so in here of
course we can't have amount be a number
of things right we can't have it be zero
and they would need to not have their
health Factor be broken so we're going
to say amount equals bound amount one
and let's say Max deposit size and then
additionally this amount better be more
than the value of the system right
because we have this revert if Health
factor is broken but maybe I don't even
want to do that narrowing down maybe I
just say screw it I'm going to make
revert on false revert on fail on revert
false and I'm just going to leave it
like that because I'm nervous that I'm
going to narrow it down too much so then
maybe I just go screw it bm. start prank
message. sender and we call in the DSC
engine mint DSC dce do mint DSC and the
amount then bm. stop Rank and this is
where actually you'll see sometimes some
people will have continue on revert and
a fail on revert fail on
revert see people some people can have
two types of folders continue on revert
is going to be the quicker looser test
where it might look like this the fail
on revert is going to going to make sure
that every single transaction that you
run your invariant test sued on is going
to pass I personally think it's good to
have both when I'm writing invariant
tests I actually start with to continue
on revert because they're faster often
times you can find bugs as long as you
narrow down them enough so for now let's
go ahead let's have this be false see if
we can find any issues like this so
we'll go up up so we'll run Forge test-
M just going to run this invariant test
boom let's see if we got any issues here
we'll see how many reverts we get as
well we got a similar amount of reverts
which is good similar amount of calls
which is all right and then actually if
we do- VV we can even see the Conant
that logs at the end it looks like it
hasn't found a way to Mint more tokens
than collateral in the system oh and
even it looks like the last run it ended
with a total supply of zero right but
maybe I'm paranoid let's actually go
back to true and let's just stay true
for the rest of the rest of this but
like I said I think it's good to have
both types of test anyways Handler so
this mint DSC we should only be able to
Mint DC if the amount is less than the
collateral so what we can do is we can
call this get account information which
gets the total collateral value in USD
and total DSC minted and make sure that
we're always going to Mint less than the
collateral value that we have so we can
say we can actually just copy these two
paste it in here equals dsce dot copy
this function paste message. sender so
we're going to get the total DSC minted
B value in USD and what we can do is we
can say we'll just have them always mint
the max DSC they can mint so we'll say
un 256 Max DSC to Mint
equals collateral value in USD we could
say the collateral value in USD / 2
minus the total DS minted better be
greater than zero right better not be a
negative number so we can even do like
an INT
256 and then if this is negative then
we're just going to return otherwise
we're going to say amount we're going to
equals bound this again say
amount Z comma Max D to Mint then we're
also going to say if amount equals zero
we're going to return oops and then
actually we need to grab this start
prank put this down here that's all we
need to do and we should probably just
grab these put them down here we don't
need both of these so we just need this
I think yeah we don't need both of these
we only need one of these so we'll do UN
2 we'll convert this back to un
256 F am out is zero
return this looks pretty good to me
let's see if we made this actually work
let's run this and now we can mint
DSC
successful and bada boom so we have some
calls we have no reverts this is
great but I keep getting total Supply is
zero down here so we plenty of we plenty
of rap Bitcoin are we ever calling this
function
H we keep getting a total supply of zero
down here why is that how could we
figure out if our mint DSC is actually
getting called let's first off see if
this mint dsse is even being called we
can use something called ghost variable
to track this so in here up at the top
we'll make a function called un 256
times mint is
called make this a public variable and
at the bottom of mint DSC we'll do times
mint is called
plus now back in our invariance we do a
console.log times mint called comma
handler. timesm called and then we'll
run our test again oops with-
VV and we can see oh mint is actually
never called how is that possible well
it must be because one of these returns
is hitting and it's not finishing this
call so we could keep moving this up to
figure out where it's actually being
called and to continue to debug this but
I already know how to debug this of
course because I've done this a while if
you want take this as a challenge for
you to answer the question why is this
never being called why is times Min
being called Never being called why is
it never finishing
so feel free to pause the video try to
debug this yourself and find out and
then I'll tell you the technique that I
used to debug this and figure out why
this mint DSC was never being
finished all right welcome back did you
actually pause the video did you
actually try to figure it out if you
didn't I'm giving you a second chance
pause the video go find out pause the
video and take this as your opportunity
to try to debug okay why is this not
getting hit why is this line not
hitting all right welcome back now there
was a couple different ways that I used
to actually debug this one of them was
having this times Min called plus equals
1 and then moving it up so I found the
line that it was breaking on once I
found the line that it was breaking on I
console. loged all the values of the
different variables around one of the
most important variables that I dumped
was going going to be the message.
sender so when you're working with
invariance remember it's going to call
this contract with a ton of different
functions and a ton of different calls
however it's also going to call them
with
random addresses as well so in order for
us to Mint DSC we need to only mint DSC
with an address that has actually
deposited collateral
because it's impossible for someone to
Mint DSC without them depositing
collateral now again if we restrict this
function like this maybe there is a case
where you can mint DSC without
depositing any collateral that we don't
know about this is again why it's
important to have some open invariant
tests some continue on revert and some
fail on revert as well so if we want to
have this be failon Reverb we would need
to only pick a message that sender that
has some deposit collateral so what we
can do is actually keep track of people
who have deposited collateral and then
when we go to Mint we just choose an
address from somebody who already has
deposited so how can we do that well we
can just keep track of an array of
addresses that
have lateral deposited so up at the top
well let's actually keep this times mint
as called we'll put it at the bottom
times M it's called Plus+ and this way
we know how to test this right if times
myth is called increases we've known we
will know that we fixed the actual issue
so times myth is called plus plus but
the top we'll do a address array public
users with collateral
deposited we'll copy this address and
now in deposit
collateral users with collateral
deposited. push message. sender now
there's some caveats here this will
double push obviously some people so
this will double push if the same
addresses push twice but for now let's
just keep it simple and let's go ahead
and just leave it like this we probably
should check to see if someone has
already deposited collateral but
whatever we're going to go with this for
now because simple and then now in our
mint DSC we can do something similar to
what we did with collateral so we'll do
a underscore U into
256 address seed
and instead of bounding to message.
Sender what we can do is we can say
address sender equals users with
collateral deposited it's going to be
the index of the users with collateral
deposited Andress seed mod uses with
collateral
deposited. length and now now instead of
message. sender we're going to use
sender in here same thing with down here
we're going to use send
now let's run this and see if mint is
ever actually
called aha so this was very helpful redo
it let's do- VV
VV ah okay so at least we're getting
something different here right we're
getting an error division or module by
zero ah okay of course we're getting
modu by zero because if the collateral
length is zero then obviously sender is
going to be zero so we can do if uses
with collateral. length equals equals z
then we're going to return we're going
to skip this one so let's run this
again okay so we're getting some stuff
passing let's actually just do a 2vs so
that it's easier to read than all those
events
aha total times mint was called is now
31 and we're getting a total Supply so
our mint DSC function in our Handler is
now actually working we're now
successfully calling mint DSC and it
looks like our protocol is holding up
all right fantastic so we're getting
closer to building this Handler to
actually have a solid Recreation of all
the possible functions we can actually
do in this system something I didn't
show you was we should pretty much much
always use a given invariant called
function invariant Getters should not
revert like this and we just put in here
all of our invariance like dsce do get
liquidation bonus dce do get Precision
Etc put all of our Getters in here and
oh and this could be public View and if
any of these revert then this will fail
this invariant test will call a ton of
different functions on the Handler and
if any of the function combinations
break any of our Getters we know we
broke an invariant this is a layup and
variant that everyone should always 100%
include a way to make sure that you're
including everything is you can run
something called Forge inspect DSC
engine methods and it'll print out all
the different methods that this function
has in addition to its function
collectors so you can kind of use this
as your checklist of all the different
functions you can call on a contract and
you can look for all the view functions
in here this is additionally why it's
great to have get in front of these
words because becomes very easy to
figure out which ones are
Getters but it doesn't reflect the whole
world right one of the other really
fantastic things we can do with the
Handler is we can both handle our DSC
engine but any other contract that we
want to simulate for as well and there's
a lot of things we want to take in mind
when writing these especially the other
contracts that we interact with what are
some of the other contracts that we
interact with well one of them is going
to be the price feed one of them is
going to be the we token the wrapped
Bitcoin token so our Handler should
probably also show people doing random
weird things with we and rap Bitcoin
right because people are going to do
random weird things with both of these
tokens and we want to make sure our
system can work with them appropriately
now I'm actually going to skip them for
now but I am 100% going to do one with
price feeds because price feeds are
definitely a system that can change and
definitely a system that greatly affects
our protocol so we're going to include
price feed updates in our Handler so
what we're going to do is we're going to
go ahead and do import a mock V3
aggregator from slash do do slash mock
SL mock V3 agregator doso or where is
this located oh just one this okay and
this mock V3 aggregator has some
functions that allow us to just easily
update and answer right which is what
something that we want to do we want our
protocol to be able to easily update
answers so we'll take this mock V3
aggregator and let's at least get the we
price from our system so so I have a
view function dsce doget collateral
token price feed and I'll pass the
address we
oops and I'll say eth and I'll make
another variable mock V3 agor public eth
USD price feed I'll say eth USD price
feed equals this and we just got to wrap
this up as a Mo V3
aggregator this and great now we have an
ethusd price feed and now we can add a
new function in here so we have mint DSC
deposit collateral redeem collateral we
can add a new one called function update
collateral price we'll do a uent 96 just
so that the number isn't too big new
price and then we could also randomize
the collateral but for now we'll just
have it be the ethusd so
we need to convert EU 96 to an INT 256
new price int equals in 256 new price
because price feeds take in 256s oop
sorry this should be in and to convert a
u 96 to an INT 256 we actually have to
wrap it as u in 256 first and then to n
96 do update answer or set price or
whatever we want to do to this new price
like this boom and now simple as that we
have an update collateral price well an
update eth price anyways so now we can
do three things in our system we can
update the price redeem collateral
deposit collateral and mint DSC so
before we actually run this what do you
think do you think we'll get n what do
you think we'll go on let's run this
oh it looks like it found a sequence it
found an issue the reason assertion
violated which means that our invariant
here was broken the we Value Plus
Bitcoin value now is no longer the total
Supply so let's scroll up let's see what
the issue is here all the way past
everything and if we read the sequence
we can figure out why this broke we see
exactly what happened okay so first it
called deposit collateral Okay cool so
we deposited some collateral and then we
minted some DSC okay cool we minted some
DSC with some stuff here and then we
updated the collateral price to
471 so as we know our Handler update
collateral price if we scroll the way to
the bottom update collateral
price
471 updates the eth collateral from
$2,000 which is
2008
to
471 so this remember is 2 1 2 3 4 5 6 7
8 it went from this to 471 so of course
it reverted right because people minted
a ton of collateral they deposited
collateral they minted a ton of DSC
right look at this input it's massive
and the system broke and if we run this
again with fewer V's right VV we'll be
able to see that total Supply we scroll
up we'll be able to see the we value rep
Bitcoin value and the total Supply here
so actually it looks like in this one it
set the new price to three so obviously
the we value is probably zero or just
about zero it let them mint DC because
originally the collateral was were was
worth something and now it's worth
almost nothing right so this is an
important thing for us to take in mind
in our system hey if the price drops or
spikes quickly our system is screwed our
system is busted and this is something
we would want to know about and
potentially go back in our code and fix
hey what do we do when the price
plummets in a single block right now we
have kind of this assumption in here
where we have this liquidation 10% bonus
and the collateral always needs to be
200% over collateralized with this we're
saying okay between 200%
overcollateralization and 110%
overcollateralization
as long as our system is within this
it's still safe I mean obviously it's
better if it's above this but as long as
our system is within this it's still
safe but if the price plummets of some
collateral and let's say that's the only
collateral maybe we get to 50%
collateralization rate and that would
break our entire system right that would
break our invariant and our system will
be screwed so we go back to the drawing
board figure out how to smooth this out
or we can say this is a known bug if the
price fluctuates or explodes too quickly
or too slowly this protocol becomes
worthless and that's kind of not a great
solution right so these are things we
absolutely want to keep in mind and
these are things that we can find with
invariant tests this is why they're so
important so for now in our Handler I'm
actually just going to even comment this
out because it does break our test Suite
but I'm going to put a little comment
here this breaks our
invariant test suite and this would 100%
be something that shows up in a smart
Contra audit saying hey if the price of
an asset plummets too quickly the
system's breaking because it breaks the
invariant all right great now there's a
few more things I want to teach only a
few and then we're done with this
section we're going to teach one some
proper Oracle use and then two we need
to write more tests which we're not
going to do I'm going to leave that to
you but we have a whole bunch of other
contracts in here like the decentralized
stable coin and then three some smart
contract audit preparedness some smart
contract audit preparation so let's
start with some Oracle proper
use so in our DSC engine we're of course
using an oracle right we're using chain
link price feeds now this is kind of an
assumption that we have in our protocol
right now he price feeds are just going
to work but price feeds are a system
just like anything else and we should
add some checks in our code here just to
make sure that if this breaks or if
something in here breaks our system
isn't broken so what we're going to do
is we're actually use that Library
methodology we made years ago to write
some checks on this price feed so I'm
going to make
a libraries folder and we're going to
make a new contract in here called
oracle. and what we want to do is we
want to check to make sure that these
prices aren't stale if we click on any
one of these prices like eth USD let's
scroll up to show more
details more details you can see they
have this heartbeat where a new price
should show up at least every 3600
seconds I believe what this is right yes
on theoa testnet we want to write some
checks to make sure that this is
actually updating every 3600 seconds and
if it's not we should probably pause the
functionality of our contract so we're
going to make a
spdx
license identifier MIT pragma solidity
like this we're going to do a library
Oracle lib and let's put a little natat
speec to explain what this is going to
do say add title Patrick Collins oops
title Oracle lib at author
Thor Patrick
Collins at notice this library is used
to check the chain
link Oracle for
stale if a price is stale function will
revert and render the dce engine
unusable this is by Design so we're
going to say hey if a CH price feed to
stale just stop don't let anything
happen because if a price is wrong if a
price is bad our whole protocol is kind
of bunked right so we want to just
freeze everything so we want the DSC
engine to freeze if prices become stale
so if the chain link Network explodes
and you have a lot of money locked in
the protocol too bad this is something
that's going to be a known issue right
if the chain link Network blows up and
all the prices become stale H you're
kind of screwed right and maybe this is
something we want to account for but for
now I'm just going to say that's a known
issue and we're going to move on and
this is where you'll see me start to get
more and more par particular about stuff
this is where as we get more and more
advanced this is where the details start
to matter more and more right all those
little little things that I kind of
gloss over they become to they start to
become more and more important as this
Bec comes closer and closer to a real
production product that should go to
audit right so let's create a stale
price check function so we'll create a
function stale price check and we'll
have this stale price check beond an
aggregator V3 interface. Soul so I'm
actually going to copy this paste it in
here toggle the word wrap and so as an
input parameter it's going to take
aggregated V3 interface price feed
this will be a public view which will
returns a uint 80 in
256 U 256 U 256 and a uint 80 the same
return value of the latest round data
function in an aggregator V3 interface
function like this okay cool and in here
what we're going to do is we're going to
call Price feed Dot latest latest round
data and I'm even just going to cheat a
little bit we're going to control click
into this we're going to copy this line
paste it here
equals boom just so I don't have to type
as much cool we have all those and what
we're going to say in here and we're
probably not even going to use all these
we're going to have some stale check
right so each one of these price feets
has their own heartbeat so we probably
should ask them what their heartbeat is
but I'm just going to hard codee it for
this one I'm going to say un 256 private
constant timeout equals 3 hours and this
is a constant in solidity it stands for
3 * 60 Minutes time 60 seconds
equals uh this many seconds so looks
like this heartbeat is actually much
longer than the one the chain link
should allow so 360 seconds is 3,600
seconds is just 1 hour right we're going
to give it 3 hours so what we're going
to say in here is we're going to say
first we're going to do a un6 seconds
since equals block. timestamp and then
we'll have uh excuse me
minus updated
app and then we'll say so this will get
the current block timestamp minus this
updated app so this should basically get
us the seconds since this price feed was
updated then we'll say if second since
it's greater than our timeout then we're
going to revert with a new
error
error or
cbor stale price this revert with stale
price and then we're just going to
return all of this stuff so return round
ID answer started at updated at answer
around and I'm going to change this name
to stale check latest round data now
what we can do since this is a library
on our price feed we can use this stale
check latest round data to automatically
check to see if the price is stale so
now in our DSC engine anytime we call
latest round data we just swap it out
for stale check latest round data so
long as at the top we go after our
errors we're going to put our types
after the errors we're going to put
types so this is where we would do using
Oracle lib or
aggregator B3
interface we need to import Oracle lib
let import Oracle lib from there's going
to be libraries
libraries Oracle li. so like this and
now yep any place we use latest round
data we can now use stale check latest
round data where we have this stale
check baked in and cool now we did a ton
of refactoring let's run Forge test just
run this whole test Suite including the
invariant test
Suite okay and stuff's looking good here
and you can see that it even took a lot
of extra time to run this last bit so
cool we've got a little check here great
we're not going to write some more tests
this is something that you should 100%
do we pull up our terminal here we run
Forge coverage what do you think we
get get this which you can see there's a
whole bunch of contracts that we need to
test this Oracle lib could probably use
its own test Suite even though it's
looking like a lot of it's tested we
probably should definitely test this
ourselves we need to write test for this
we probably want to test our uh our DSC
some more for sure so we should
definitely write some more tests I'm
going to leave that to you this little
little finger
here and then finally some smart
contract audit preparation so we talked
a little bit about what a smart contract
audit is and we haven't covered a whole
lot of security stuff yet we're going to
do that later in the course but a solid
place you can look is this audit
Readiness checklist from the nent XYZ
GitHub repo which has a lot of different
things that you should and keep in mind
when running your tests for those of you
looking to be really serious about
actually launching a protocol and really
having the security mindset that you
need as well be sure to get to this last
section in the course about intro to
security because this is where we're
going to give you a lot of that lower
level security stuff at least from a
smart contract developer perspective
we're going to give you all the basics
that you need to be aware of in order to
stay secure so we're not going to talk
about it too much in this one but it is
something that if we were to actually
launch this we would need to keep in
mind so I'll put a little soon Emoji
here for coming very
soon all right so with that all being
said we've done an absolute ton here
this is
1,000% a project you should push up to
your GitHub repo and this is 1,00% a
project that if you made it this far you
should be incredibly proud of yourself
this is the hardest most complicated
most advanced project in this course and
to be honest probably the most advanced
project you'll work on in almost all of
web 3 there's so much going on here we
learned about defi we learned about
advanced
state-of-the-art modern fuzzing
techniques we learned a tiny tiny bit
about security we used Oracles in a
safer way we wrote this crazy amazing
test Suite we wrote deploy scripts for
this we wrote We interacted with a
couple different libraries we learned
about this failon revert runs depths and
variance the only thing we didn't do was
write a proper read me which you 100% %
should write a proper readme and if you
want you can check out The Foundry defi
stablecoin readme to see how it actually
works of course even for me this was a
long difficult project for me to build
because there's just so much to think
about like I said this is a project that
I am planning on getting audited so what
you're going to see in this repo is
you're going to see kind of this main
branch which is what you're going to be
working on but I'm additionally going to
make a new branch called like audited or
post audit or something something like
that and if you want to follow and watch
this GitHub repo you can see the
progress and you can see the audit
reports that come out on here so that
you can be intimately familiar with this
codebase already because you wrote some
of the code and then also see as it
progresses through its security journey
and for those of you who are looking to
actually release production code you
definitely need to be at least aware of
how security works and the security
paths that your code should take but all
right
with all of this being said you know
what time it is it's time for you to get
a break you deserve it you should 100%
go take a lap push this code base up to
GitHub and actually clean it up a little
bit I'm going to be cleaning this up a
little bit before I push the rest of it
to this GitHub repo so you should clean
it up a little bit make it yours make it
the way that you want to make it maybe
even improve on it right we saw with the
invariant test that there is at least
one other glaring issue with this
protocol if the price of the assets
collapse too quickly our protocol
becomes insolvent so maybe you come up
with a method to fix it and then maybe
you launch your own stable coin why not
right in any case good luck to you take
that break we only have we're getting so
close we only have one two three more
lessons and these ones are actually
easier than the one we just did we're
going to learn about upgrades and
proxies we're going to learn about
governance and then we're going to do an
introduction to Smart contract security
these are much easier than everything
you've done so far so take the break
give yourself a pat on the back be
incredibly excited celebrate this win
this is a huge achievement getting this
far and I'll see you very soon three
more
left and as a bonus piece of content
here another one of the reasons that I
absolutely love the a protocol and the a
team is that they're just shipping
protocols and shipping amazing products
and features and services for the web3
community one of those protocols is
something called lens protocol which is
a decentralized social layer or a
decentralized social platform for
building social medias so to give us
some information about this we have the
head of devell for lens protocol and The
A Team Nat dbit to talk a little bit
more about lens hi my name is Nat dbit I
wanted to give you a quick introduction
to lens protocol and why it might be
interesting to you as a smart contractor
solidity engineer lens is the social
layer of web 3 it allows developers to
build social applications or to
implement social features into their
existing applications there are 4.9
billion people in the world today
already using social applications so
these types of apps provide a use case
that people already know understand and
value they also present a wide variety
of value propositions and opportunities
for developers to take advantage of and
build on and with web 3 features like
native payments ownership and
composability also provide a lot of
Primitives to build on that were not
available with traditional social
applications or infrastructure lens
allows developers to extend the core
smart contracts by building out their
own custom modules this would be similar
to as if Twitter Instagram or other
social applications allow developers to
send pull requests into their backends
and apis this this opens the door to a
lot of interesting and Powerful
functionality that we're seeing
developers integrate into their
applications build out new different
ideas but also integrate into other
parts of web 3 like defi in addition to
that you can call lend smart contracts
from other smart contracts so if you'd
like to build out something that is
composable with the web 3 social graph
lens is a great place to integrate if
you want to get started building on lens
check out the docs at docs. lens. XYZ
and be sure to check out how to deploy
the product call on your own so you can
check out the smart contract code and
play around with it and also look at how
to build out and create your own custom
modules thanks for checking this out
all right and welcome to lesson 13
boundary upgrades where we're going to
learn about upgradeable smart contracts
the pros and the cons now I've actually
done a couple different versions of this
type of video in the past so we're going
to go through an old video explaining
the concepts of this then we're going to
move over to remix and we're going to
explain an important subl lesson and
then we're going to go to our vs code
and we're actually going to do all of
this in Foundry upgradeable smart
contracts are something that I'm going
to go ahead and just say right out loud
we should deploy deoy as little as
possible we should not be defaulting to
upgradeable smart contracts it sounds
like a good idea in theory but we've
seen time and time again whenever a
protocol has a centralized control over
contracts issues Happen full stop so
when going through this course and when
learning about this keep this in mind
what are the downsides of upgradable
smart contracts and then you can begin
to ask questions have we seen examples
in real life where these downsides have
come to a fruition and I'll say right
now 100% the answer is yes so keep that
in mind when watching this and use this
knowledge to make yourself be an even
better smart contract Dev so let's
learn now I'm editing this video much
later after I filmed it 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 those smart
contracts are immutable or unchangeable
but what if I told you that they were
mutable well technically I 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 and 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 Dey 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 we 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 trailer bits articles that help me
put this video together uh 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 then
and 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 in mind 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 de centrality so we
need to keep that in mind and this is
why it's so important that before before
you go ahead and jump in and start
deploying upgradeable 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 SLP
parameter 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 subcategory like
metamorphic contracts transparent
upgradable proxies and Universal
upgradeable 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't add new
storage or state variables so this is
really not really upgrading but it is
something to think about 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% every year or something like
that maybe we have a set of function
that says hey update that to 2% or
update that to 4% it's just a Setter
function that changes some variable now
the advantages here are 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 uh 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 AA Used before
you call a function you actually check
some contract Reg indry that is updated
as a parameter by somebody and you get
routed to that 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 our
upgrades but some people might even
think that upgrading your smart contract
is ruining the decentral and one of the
things that Mak smart contracts 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
customers ation or any upgrade ability
you should deploy your contract and then
that's it trb bits 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 setting 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 Yeet method and now I think
it's less funny so we're just going to
stick with social migration the social
Yeet method or the migration method is
just when you deploy your new contract
not connected to the old contract in any
way 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 and over
into using this new one that the upgrade
is done hence my slang name of social
Yeet because you Yeet the first one out
of the way and you move to the the
second
one I think I'm funny this has the
advantage of truly always saying hey
this is our am mutable smart contract
and this is our new one this is really
the truest definition of a mutable
because since you give it no way of
being upgraded in place then if somebody
calls that contract in 50,000 years in
the future it'll respond exactly the
same another huge disadvantage here is
that you have to have a totally new
contract address so if you're an erc20
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 the 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
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 new version of the
contract so there is a ton of social
convention work here to do trailer bits
has actually written a fantastic blog on
upgrading from a V1 to a V2 or Etc with
this Yeet 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 an easy way which
leads us to our big ticket item the
proxies what's our big ticket
item proxies it's people 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. sender and message. 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
fall Bank 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
implementation 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 contract 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 the
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 variabl 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 type 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 gotas and we're
going to talk about the gachas 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
tril bits 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 gotas 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 two but these smart
contracts are actually kind of dumb we
actually set the value of whatever is in
the same storage location on contract a
as contract B so if our contract looks
like this and we have two variables in
contract a we're still going to set the
first storage spot on contract a to the
new value this is really important to
know because this means we can only
append new storage variables in new
implementation contracts and we can't
reorder or change old ones this is
called storage clashing and in the
implementations we're going to talk
about they all address this issue the
next one is called function selector
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 the
function selector is a 4 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 same 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 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 app this
leads to our first out of the three
implementations of the proxy contracts
this is called the transparent proxy
pattern 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 ad functions if you're a user
you're calling implementation functions
so if you're an admin and you build some
crazy awesome di5 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
[Music]
UPS this version of upgradeable
contracts actually puts all the logic 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 Yeet method with
you in 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 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 multi-
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
ethereum improvement proposal and most
of them are in the draft phase and at
the end of this explain 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 look at
delegate
call now we're going to learn about how
to actually build these proxies how to
build these upgradeable 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 hard at nfts we have a
su lesson in there about evm op codes
and coding and calling and will 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 minimum istic real simple 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 bars and updates are ENT
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 our
uint public num is at index0 our senders
at index one our values at index 2 Etc
now we're going to deploy a contract a
now this contract is actually going to
use the delegate call function now in
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 the
difference here is we're doing contract.
delegate call what this call does is
something very similar to call normally
if we did contract. call on this
contract we would just call this we
would just be calling this function set
bars which would update contracts B's
storage but instead we're saying hey
call that set bars function and then
pass this as an input parameter but call
it in our contract 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 numb our contract is going to
say hey whatever is that storage of Zero
have that equal to whatever we pass as
an input parameter and if that's a
little 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 byy
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 B we'll hit deploy
we now have a contract num sender and
value or 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 numb value incenter are
also all blank but when we call set bars
it's going to borrow this set bars
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 it again
that's what this delegate call function
does so when I call set vars I'm going
to pass it this contract address as an
input parameter so it knows to call this
contract set vars 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 bars we see
num now has 987 we see sender 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 storage 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 sender we could call
something else and then value we could
call woo or whatever you want here and
when we borrow this function using
delegate call instead of us 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 numb variable which is
which is at storage slot zero so when we
borrow set bars in contract a with
delegate call we'll
say storage
slot Z is going to equal that underscore
num which for this contract storage slot
Z is first value so we'll say first
value equals uncore num something else
is going to be storage Slot 2 so it's
going to say okay storage slot two we're
going to update storage slot two to
mess. Sender okay value here is storage
slot three so whatever's in storage slot
three will update with message. value
like this so that's essentially what's
going on behind the scenes so let's go
ahead let's delete those and
redeploy deploy them so we'll deploy
contract B we'll deploy contract a right
now in B once again if we do 1 2 3 for
set bars we have 1 2 3 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
bars and first value is now 654 so
delegate call 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 slots 0 1 and two would still
get updated now here's where things can
get really interesting let's delete our
contract a again and let's change the
type of our contract A's first value to
from a uent to 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 uint and assigns the first
storage slot that number we pass it but
our first storage slot is now a Boolean
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
222 as our input parameter we'll hit set
bars our transaction actually does go
through and now when we look at first
value it says true huh that's really
weird what if we change set bars to zero
and hit set bars 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 actually get some
really weird results if your typings are
different or if your storage varials 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 going to be working
with once again in the hard Hut upgrades
FCC subl lesson small proxy.com
if you want to just move on to learning
how to actually build these proxies
without really 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 doou thing from open Zeppelin
open Zeppelin 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 Ule and it's an intermediate
language that can be compiled to bite
code for different backends it's a sort
of inline assembly inside solidity and
allows you to write really really lowl
code close to the op codes now we're not
going to go over Ule but I'll leave some
links to the Ule documentation if you
want to learn more even if you're a
really Advanced user you really want to
try to use as little Ule as possible
because since it is so much lower level
it is much easier to screw things up
however like I said for this example we
are going to be using a little bit of
Ule now in this proy that we're going to
be doing we have this delegate function
which inside this inline assembly which
is Ule it does a whole lot of really
lowlevel 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 whatever it receives a function it
doesn't recognize it'll call fallback
and fallback calls are delegate function
so anytime a proxy contract receives
data for a function it doesn't
recognizes 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 could be equivalent to
like upgrading your smart contract and
then we have implementation here to read
where that implementation contract is
now to work with proxies we really don't
want to have anything in storage cuz if
we do delegate call and that delegate
call changes some storage we're going to
screw up our contract's storage the one
caveat though to this we do still need
to store that implementation's address
somewhere so we can call it so EIP 1976
is called the standard proxy storage
slot which is an ethereum improvement
proposal for having certain storage
slots specifically used for Pro
and in our minimalistic example here we
set by 32 private constant
implementation slot to that location in
storage and we'll say okay whatever is
at 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 going to build here so we have
this small proxy is proxy and we'll
create a real minimalistic contract so
we'll say contract
implmentation a and we'll just give it a
un 256 public
value and then function set value un 256
new value public we'll say value
equals new value and so this is going to
be our implementation so now anytime
somebody calls small proxy our small
proxy contract it's going to delegate
call it 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 we'll do
function get data to transact
and we can get the data using the ai.
encode with signature that we learned in
an earlier lesson so function get data
to transact we'll pass it to un 256
number to update so we'll give this the
number we want to call a new value we'll
have this be a public pure that's going
to return a
bytes memory and we'll just say return
ai.
encode with
signature set value uint
256 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 do
encode ai. encode with it signature and
call anything with its raw bytes we're
going to get the data to transact and we
know that when we call implementation a
from our small proxy we're going to
update our small proxy storage so we'll
create a little function in solidity
just to read our storage in small proxy
so we're going to say function read
storage this will just be a public view
we'll do
returns returns you went 256 value at
storage sloted 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 SL load op
code to read the value at storage slot
0o we'll say value at storage slot 0o
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 SL 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 parentheses here sorry so
now so 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 call this
proxy contract we're going to delegate
call the functions over to here so we're
going to grab implementations A's
address paste it into set implementation
777 so this is the data of set value un
256 with that number to update and coded
in it so if we call our small proxy with
this data our proxy contract is going to
go oh okay this is a function uh I don't
I don't see that function here we're
going to call our fallback right which
again is coming from open Zeppelin and
our fallback is going to do this
delegate which is this lowlevel stuff
but it's basically just doing a delegate
call we're going to call our fallback
function and then we're going to get the
function in the implementation a we're
going to borrow this function and we're
going to use it on our on ourself so if
I copy this the implementation has been
set to being this address down here so
all the logic is going to be down here
so when I go ahead and I grab this I
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 1 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 on set 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 with 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 lowle call
data and I hit transa 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 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
important 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 set
implementation gets called because 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 this is
where the transparent proxy that we're
going to be working with can help us out
here and the up Universal upgradeable
proxy can help us too and I'm not going
to go too much deeper into these now but
we've 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 upgradeable
can get around these if you're confused
by anything in here go into this
discussions thread and make a new
discussion about proxies make a new
discussion about the assembly about the
Ule set implementation this is a great
time to connect with other people taking
the course and ask questions here cuz I
know that this is a really Advanced
section and requires you having gone
through a lot of those subl 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 a
section where 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
all right so now that we've learned all
about this delegate call function and
what it can do and the power of it let's
actually put it to use let's actually
build an upgradeable contract example so
we can really see this power so let's go
ahead and let's start doing this of
course all the code that we're going to
be going over with is available in the
GitHub repo associated with this course
if you want to follow along so let's go
ahead make a new directory boundary
upgrades
f23 let's code boundary upgrades f23 or
file open folder you know the drill now
I know in that video we talked a lot
about this transparent proxy pattern
versus the uup UPS we're actually going
to be teaching the U PS Pro in The UU PS
proxy the upgrade is handled by the
implementation and can eventually be
removed and this is incredibly important
especially if we want to heed my warning
I made at the beginning where we want to
really do this as little as possible and
this is kind of a crutch to get started
and we've seen issues where if you have
upgradeability and you have a
centralized entity that can upgrade and
change your contracts those are often
exploited so the U UPS proxy allows us
to eventually remove the upgradeability
so that the code is truly immutable and
also U ups proxies are technically a
little bit cheaper to deploy you can
read more about the differences between
the two proxy types in the documentation
for open Zeppelin and additionally if
you go to the contract wizard in open
Zeppelin most of these types in here
have a upgradeability option with
ufs that we could even open in remix and
see exactly what some of these might
look like so we're going to be going
through that with our our own example
here so we're going to go ahead we're in
our folder we're going to do Forge
knit of course and we're going to go and
delete these once again bye goodbye
goodbye delete with the trash I held
down command so I can select all three
and let's create a very minimal contract
that we're going to actually upgrade so
we're going to say a box V1 do soul and
this is going to be what we're going to
upgrade so in this box V1 we're going to
do
spdx
license identify MIT you know the
drill pragma solidity 0.8 r with a
little carrot
here contract box V1 like this cool now
so since we're using the U UPS proxy
we're actually going to need to add all
of the proxy upgradeability into this
contract as well so we can actually
borrow from open Zeppelin kind of like
what we're seeing here because they have
all of these contracts already created
for us but let's actually just write out
what our what we'd want our box to do
first and then we'll go ahead and do
that so we'll say a un 256 internal
value or number a little bit less
confusing we'll have a function get
number public view or better yet
external external view returns un
256 turn
number all of this is something you all
get because you did the stable coin
video which was a monster of a project
version external your returns you
256 return one and that's it this is all
our box is going to do now let's copy
this box let's go to our source we'll
create a box v2. Soul paste all that in
here and let's change this a little bit
let's have the version return two get
number will still return number but
let's add a function function
set number un
256 number this be external null like
this but so here's oh and this rename
this to box V2 Okay cool so now we have
our box V1 we're going to deploy box V1
and and then at the contract address of
box V1 we're going to upgrade it to box
V2 now remember though if we upgraded it
changed some storage variables we would
mess it up right so if you go to the op
zeppin documentation let's actually
reopen this back up in remix here we can
see kind of what's going on here so this
token example that they have they say
it's initializable upgradeable ownable
and uu PS upgradeable they say it's all
this stuff so for us we're going to go
ahead and we're going to install this
open Zeppelin contracts upgradeable
package and this is actually at a
different open Zeppelin repo we look up
open Zeppelin contracts upgrade
GitHub there's this open Zeppelin slopen
Zeppelin contracts upgradable repository
that we can use and this is the one that
we're going to want to install so we're
going to copy this like that Forge
install this this D- no-
commit all right great we've installed
it now we can start using these
contracts upgradable code in here let's
just go in here do a rem mappings equals
we'll say at open Zeppelin SLC
contracts
upgrade a
equals lib
slop dein and let's do a toggle word
wrap Zeppelin contracts gradable SLC
contracts hopefully that looks right
let's go to lib opens up a contracts
upgradable yep that looks right okay
cool so now we can start using this so
in this remix example it can be a little
bit confusing because there's a lot of
imports going on right so let's kind of
break this down so the first thing that
we're going to need need is going to be
this U UPS upgradable contract and I'm
just going to copy this line feel free
to copy this line as well or you know
write it out yourself and I'm going to
toggle the word wrap here of course
we're going to do named Imports because
we're good Engineers uh what did I mess
up oh I don't need this versioning okay
cool and if we control click into this
or we command click into this or we just
open this we can see exactly what is in
this upgradeable code here we have this
uyu PS upgradable contract it's
initializable it's some proxy stuff you
know yada yada yada we scroll down got
some
modifiers it has this function upgrade
to address new implementation upgrade to
and call and it has this Gap at the
bottom so let's talk about these so this
upgrade 2 function this is the function
that gets called when we want to upgrade
our box so we're going to say our box is
uus U UPS upgradeable and then it's
going to say hey uh should be marked
abstract because we don't have all of
the functions defined in here and this
contract is actually known as an
abstract contract abstract contracts
have some functions defined and some
functions undefined and they expect
their child classes to implement those
functions so for example at the bottom
here we have this authorized upgrade
function which isn't defined so an
abstract contact basically says hey you
better Define this authorized upgrade
function because I'm not going to do it
you need to do this and this can be
helpful for a lot of reasons right for
example let's say you want to make sure
to not forget to add some function so
this is something that we're going to
need to Define these are how we're going
to actually do the upgrades we see that
this is you know obviously the function
that we're going to have to Define so we
can add authorization parameters in here
such as who can actually do the
authorization so if I toggle the word
wrap we can see function that should
revert when message. sender is not
authorized to upgrade the contract so
for example if you wanted your proxy to
be owned by a dow you would update this
to be ownable or only owner or something
like that but then they call this
upgrade to and call U UPS which if we
control click into or we just go to
where it's defined uh we toggle the word
wrap here we can see this performs the
implementation upgrade with Security
checks for ups proxies and additional
setup so if we scroll into here this is
the code that's actually doing the
upgrade and it's really low level right
so we're call this function set
implementation which now routes all
contract calls via delegate call to this
new implementation address that we set
and then it does a couple of checks in
here and then it calls upgrade to and
call which which calls upgrade two which
call set up
implementation which mainly just sets
the implementation slot which basically
like I was saying before it says okay
all contracts are going to be routed to
this address now right but if we go all
the way back all the way back that's
kind of the long of what this upgrade 2
is going to do right so it's going to
change where we route all of our
delegate calls and then if we have any
data we want to send it'll call this as
well the data is obviously going to be
none uh if you just call upgrade to
anyways I'm getting kind of in the weeds
here right this is the function that it
has set up to to do the proxy stuff we
showed before right this changes the
implementation address so we obviously
need this we also need to implement our
own authorized upgrade function so we
can just go ahead and copy this and in
here we'll paste it in here and for us
uh we need to make this override instead
of virtual I'm just going to leave it
blank because I don't want any I don't
really care I don't want any
authorization stuff in here right but
this is where I put like you know
require or excuse me if you know
message. sender does not equal owner you
know revert right right but for us I
don't really care so I'm just going to
leave it blank right and so this is
going to say anybody can upgrade this so
this is why this is going to be the most
important one right this U UPS
upgradable is doing all that proxy
upgrade stuff all right cool so what
else now you'll also notice here at the
bottom there is this Gap thing now you
can read the docs here this empty
reserve space is put in place to allow
future versions to add new variables
without shifting down storage in The
Inheritance chain what what what what
the heck what are you talking about
storage gaps the reason these proxies
work is that they point to the slot
number instead of the name of variables
so if I were to upgrade from box V one
to box V 2 and I put in another other
number here guess what if I did this
upgrade whatever number number was in
box V one is now set to other number
instead of number this is because
proxy's point to storage slots they
don't point to variable names they point
to storage slots if we upgrade to V2
other number is now in storage slot zero
whereas number was in storage slot zero
over here right so what is the best
practice to do is to have this Gap at
the bottom so a u into 256 you can put
really whatever you want here which
basically just says heyy we're going to
save 50 storage slots in storage for
adding new variables in the future you
could have this be you know 100 you know
whatever whatever amount you want right
and this is just so that in the future
if you need to add more storage slots
you don't collide with other places that
storage is already being reserved so
it's really important that they have
this Gap especially for U UPS if this
doesn't make sense definitely 100% be
asking questions about it and you'll see
a lot of people will ma even manually
add their own gaps sometimes so
sometimes you'll even see somebody go
you know like Gap two or whatever they
want right so that they have additional
gaps whatever they want to do cool so
this is why we need
this now right like this we could ship
this code and this would be upgradeable
now of course box V2 is not upgradeable
um but box V one would be upgradeable so
why
is remix adding all this other stuff
what is initializable well let's copy
this and let's paste it in and you can
also see all this weird initialized
stuff what what what is going on with
that well let's let's add this in and of
course we're going to do a named proxy
if you want to write this out yourself
feel free uh excuse me we're going to
remove this because we did not add the
version in here uh initializable so what
is this why is open Zeppelin telling me
I got do I got to do this initializable
thing and it's doing all this weird
initialized stuff so let's go into
initializable oh and if it doesn't let
you do that let's open up our file
explorer we'll look for initializable
Doo and let's see let's read this so if
you want to pause and you want to read
this I'm even going to toggle the word
wrap if you want to pause and you want
to read this feel free to do that but
the main thing we know is this piece
here since proxy contracts do not make
use of a Constructor it's common to move
Constructor logic to an external
initializer function might be thinking
wait wait wait wait what they don't use
a Constructor remember storage is stored
in the proxy not the
implementation so we have proxy which
will direct I'll call to the
implementation but it's like borrowing
the implementation functions right so
all the storage is happening over here
if the implementation has a Constructor
and it sets number to one right the
proxy will have number still set to zero
cuz the when you deploy a contract The
Constructor gets called from the
implementation so proxy contracts or
contracts that are meant to be used via
proxy don't use the Constructor so what
you need to do is then with a proxy you
need to First you need to deploy
implementation then you need to call
some initializer
function now the initializer function is
going to be essentially your Constructor
except for it's going to be called on
the proxy so that's kind of the
difference if that doesn't make sense
please ask questions about it because
it's really important so Constructors
are not used in proxy contracts so what
a lot of upgradable contracts do is they
first they call this disable
initializers function in Constructors
like you see here and I'm actually going
to copy this paste it into my box View
one like this and then often you'll see
this comment I'm going to paste this
over here because sometimes different
lters will say hey you're using a
Constructor in a upgradeable contract
you shouldn't do that so we just say hey
like let it happen right what this
disable initializers function does is it
says hey don't let any initialization
happen so if we go see this we say
require that you're not initializing
right you can't call any initializer
functions so you could just as easily
just just do this right they're kind of
the same but this is much more of verose
right this is making sure hey don't do
any initializers Constructor don't do
anything so this and this are almost the
same but this is more verbose as you
know verbose is better but let's say we
do want a Constructor what we would do
is we' add an initializer so we do
something like function
initialize public initializer
initi
initializer and of course we'd say our
boxy one is initializable public
initializer like this and we would add
whatever we want to initialize and then
what we're going to do is we're going to
deploy the contract and then we're going
to have the proxy immediately call our
initialized function instead of doing
this Constructor instead of the
implementation having the storage we
want the proxy to have the storage
typically you'll see initializer
functions have two underscores like this
so if we want our box V1 to have an
owner right we can't set storage we
can't do like owner equals message.
sender in the Constructor like we
normally do with a ownable contract
instead we need to put that in the
initializer function so what a lot of
people will do is they'll add double
underscore like ownable uncore in it
like let's say we did want this to be
upgrade uh ownable and actually let's go
ahead and add that right now we're going
to add this ownable upgradeable I'm
going to paste it
in remove that ownable from like this
comma I know I kind of was quick but I
uh I know you all are Advanced enough to
kind of know what I just did here I
Factor this a little bit so that's a
named import remove the version and now
we're doing this ownable upgradable
instead of ownable I'll explain what
that means in a second oh and this
actually
goes here a note if you see this
linearization of inheritance graph
impossible this means that we're trying
to inherit stuff in the wrong order so
we'll just do this oh is that the right
order no that's still the wrong order
that the right order cool that's the
right order okay so what what you'll see
is these upgradeable initializable
functions will be prepended with double
underscores right and so what this is
doing is it's sets the owner it's
setting the owner to message. sender and
if I command click into this or command
click into this we look up ownable andit
we see it does this ownable andit
Unchained all it's doing is calling
transfer ownership right that's it this
is essentially the same as setting owner
equals message. sender right but we're
just just prepending with the double
underscore to say hey this is an
initializer function treated as such
right it should only be called in this
initialize section additionally most of
the time you'll see this initializer
function which doesn't do anything but
it's best practice to have this in here
to say hey this is a u UPS upgradeable
contract we're going to treat it as such
okay so you'll see this in here a lot
too but I know I kind of went through
this a little bit quickly but let's just
do a quick recap of what we learned we
can't use Constructors with our proxies
a Constructor adds storage to the
implementation and if we have a proxy
pointing to an implementation the
implementation would have the storage of
where the owner is and we need the proxy
to have the storage of where the owner
is so we need to call initialize through
the proxy not through the implementation
the initializer function is essentially
a Constructor for proxies you can almost
think of it like that we want to preen
them with this double underscore to know
that they're
initializers and we don't want any
initializers to run in the Constructor
and we only want them to run once you'll
also see this initializer modifier which
basically just says hey you can only
initialize one time right a contract can
only be initialized once otherwise it'll
error great and that's pretty much going
to be our box so now we made this
ownable so since this is ownable down
here in our authorized upgrade we could
add an only owner modifier in here so
that only the owner could upgrade it if
we wanted to but we're not going to add
that but I wanted to add this ownable in
it here just to show you hey like so
this is what you do instead of working
with Constructors so that's our box V
one let's fix our box V two so our box V
two we're going to do pretty much the
same thing so we're going to grab all
these paste it in here I'm going to
toggle the word wrap we're going to make
this
initializable uh ownable
upgradeable and UPS
upgradable is like this we're going to
need to add authorized upgrade which
we're going to keep blank but if you
want to play with this you can obviously
implement this we're going to want to
add a Constructor and an initializer so
we're going to copy both of these and
just paste it right in here a
Constructor which disables Constructors
and then an initializer which is going
to be really what we want and that's
pretty much it that's the only changes
that we need to make now we can make
make this even smaller right we could
say okay cool we don't care about
upgrades we don't care about
initializing and we just say goodbye
this doesn't need initializer this
doesn't need any of that stuff and we
could even delete all this if we wanted
and now it's just UPS upgradeable right
without any initializer and without any
Constructors and we could do the same
thing with box VI one but I just wanted
to demo what it look would look like if
you did want a Constructor but now that
we've done all this work
let's actually show you this in action
so we're going to deploy box V1 we're
going to get an address we're going to
use that proxy address to make sure that
these functions work we're going to then
deploy box V2 we're going to point our
proxy to box V2 which will essentially
mean we upgraded our contract and it's
all going to be from the same address so
let's do this we're going to make a
little two scripts we're going to make
one called deploy box. s soul and a new
one called up upgrade box. s.o so we're
going to deploy the box and then we're
going to upgrade it so let's deploy it
spdx you know the drill
license ragma solidity
0.818 contract deploy box is script
import script from Forge STD script.
so we're going to need to imple
import box V1 from
docbox V1 doso function run
external external returns I'm going to
I'm going to return address and you'll
see why in a bit returns address address
proxy equals deploy box turn proxy and
I'm going to create a deploy box
function function deploy box so this is
a little bit different this will be a
public returns
address and what we're going to do is
say box V1 box equals new box V1 this is
going to be our what our implementation
this is going to be our logic right this
is where our proxy is going to point to
delegate call right to borrow those
functions now we're going to need to get
a proxy on top of this the pro proxy
that we're going to use is the ERC
1967 proxy and we can learn more about
this proxy in the open Zeppelin
documentation we can learn more about
this at the EIP for ERC
1976 whatever you want to do but it's
going to be this proxy that we're going
to use to point to our implementation
right to point to our box V one which
has all this upgradable logic in it so
we're going to import that we're going
to import ERC
1967 proxy from and this is actually
just a regular open Zeppelin contract so
we're going to do Forge install open
Zeppelin slash open Zeppelin contracts
this is just a regular one contracts
oops uh D- no- commit we're going to go
to our boundary. toml and this is a
remapping
comma open Zeppelin SLC contracts equals
lib slopen
Zeppelin open Zeppelin Dash contracts I
think that's what it is right lib open
Zeppelin contracts and opens up on
contracts upgradeable okay opens up on
contracts SLC contracts like this and oh
Port this from
atop Zeppelin contracts proxy RC
1967 uh like this so this is the proxy
type we're using now we're going to say
ERC 1967 proxy equals new erc1
1967 proxy and if we command click onto
this or control click or just look at
this this does have a Constructor we
pass in the logic and the data so we
basically say hey proxy this address is
going to be the implementation the logic
that you're going to use and then any
data you want to pass to the initializer
right if we look at upgrade to and call
call upgrade two and then if there's any
initializers call that afterwards right
uh we're not going to have any
initializer stuff so we're not going to
do that but we're just going to say
address box because we just deploy this
box no data this is where like I said
this is where we' put the initial add
stuff we wanted but then we're just
going to say return and address proxy so
cool and let's just make sure stuff is
working Forge
build or at least we're going to make
sure code is compiling and source file
that's not specified required compile
version that's cuz we haven't
implemented that that's fine cool this
looks good and we're going to write some
tests to show you this actually working
in a little bit
but cool so we have a deploy box let's
actually write an upgrade box script now
and then we'll write a text
to show both of them spdx
license identifier MIT pragma solidity
0.818 contract upgrade box is script
import script from forg STD script. Soul
script scripts cool function run
external
returns address
I'm going to have this
external oops I'm going to have this use
the most recently
deployed box so to do that we're going
to actually
install Forge
install chain Excel org D boundary
devops D- no- commit like I said there
might be a better devops tool than this
you can find the recommended devops tool
the GitHub repo associated with this
recommended tool is going to be Foundry
devops so great so that's what we're
going to use I've worked with this one
before so we're going to say address
most recently deployed equals devops
tools. getet most
recently deployed going to be deep me
and this is where we're going to get the
ERC
1976 proxy let me oops toggle the word
WP here move this down on block dochain
ID oh let's import that as well import
from oh oh and I got to go to The
Foundry
doo do a little bit from libf Foundry
devops
SRC devops tools. s looks good get most
recently I spelled that wrong get most
recent deployment so we're going to get
the most recently deployed
proxy first and you'll see Y in a second
and first we're going to actually deploy
box V2 so we're going to import box V2
from SRC boox v2. Soul oops s so we're
going to do a little vm. start
broadcast did we do that in box deploy
box oh we should do that here back in
the deploy box we're going to do vm.
start broadcast
and then a stop
broadcast that okay bm. St broadcast in
the upgrade box we're going to say box
V2 new box equals new box V2 bm. stop
broadcast and then we're going to say
address proxy equals upgrade box and I'm
modularizing this for our test cases but
we're going to say we're going to create
a new function called upgrade box where
we're going to pass pass in the proxy
address right this is the proxy address
and then the address of our new box and
then we're going to say return proxy so
we're going to create this function
upgrade box which has our previous proxy
and then the address of our new box for
us to do so we're going to say function
upgrade box address proxy address uh
address new box this be a public returns
address do vm. start broadcast so okay
so we're going to call upgrade box we
have the proxy address and we want to
call upgrade on this right since this is
just an address we can't call upgrade on
an address we're going to need to call
upgrade on using the ABI of box V1 right
so let's import that as well import box
V1 from Sr cbox V1 doou we're going to
say box V1 proxy equals box V1 of the
proxy address like this and what we're
going to do is now that we've given our
proxy address box V1 ABI we can now just
call proxy do upgrade 2 since box V1 is
U UPS upgradeable means it has access to
this
function address new box this we're
going to upgrade it and we're just do
vm. stop
broadcast like this we could also
optionally wrap this in box V2 or we
could you know do a direct function call
whatever we want to do now we'll just
return
address
proxy and that's it right so what is
this doing when we actually upgrade well
we deploy Box 2
implementation we are calling this
upgrade box function and what we're
doing is all we're doing is we're saying
hey point the proxy to this new place
that's it we're saying proxy contract
now points to this new address that's it
don't believe me all right let's write a
test so new file and we're going to
write some crappy tests here we're just
going to do the whole Suite just to show
you the power deploy and upgrade test.
T.O you know the drill
spdx license identifier MIT
contract deploy and upgrade test is test
import test from Forge STD test. Soul
fragma solidity
0.818 we got to do a whole bunch more
Imports we're going to need to import
deploy box from do script SL deploy boox
dos. sole import upgrade box you know
this is what we're actually testing from
do/ script D upgrade box. s.o readying
to import box V1 from yep thank you
GitHub co-pilot and box V2 we're
probably going to need to import more
stuff but it's fine function set up what
do we want to set up here well let's get
a deploy box public deployer upgrade box
public
upgrader we'll do an address public
owner equals make
addr owner we're going to say deployer
equals new deploy box upgrader upgrader
equals new upgrade box okay cool and we
probably should write a whole bunch of
tests but for this video I'm just going
to write
one say box V one public box V1 and
we'll say box V one equals deployer do
run we're going to have to wrap box V1
like this boom so we have our box V1 it
gets deployed our deployer run is a
deploy BLX run Returns the address of
the proxy not the address of the Box V1
right Returns the address of the proxy
not the box vi1 so we can even call this
to make it more clear box vi1 public
proxy since this is actually a proxy
address actually let's do this even
better address public proxy and this is
why before we working with addresses
because it can be kind of confusing to
know what it is the proxy right the the
proxy and it right now points to box V1
cool hopefully that's more clear so now
we'll do a function test
upgrades like this and we'll just test
the whole darn system so we have the box
we have the proxy address point to box
V1 now in our upgrade box in our run we
went ahead and did all this but for now
let's just use this upgrade box
functionality because so we can so I can
show you a little bit more granularly
what's actually happening right so test
upgrade let's let's deploy our own box
V2 we do it in run we could 100% do it
in run but I want to do it myself so I'm
going to say box V2 equals box V2 or Box
2 equals new box V2 vm.
prank box 32 box two now I'm going to
say address proxy equals upgrader do
upgrade box proxy which is just an
address address of box two so our
upgrade box upgrade box here all it's
doing is saying okay cool get the proxy
for box V1 and now point it to V2 so
we're calling that in our test here and
now we could do unit 256 expected value
equals 2 and we could say assert equal
oh actually we don't we shouldn't even
set this let's not even set this right
so this address is going to be the same
we're never resetting this address we're
being very clear about that boom proxy
deployed here boom we're never even
we're never Chang the address so it's
always going to be this address now we
can do though is we can Sayer equal
expected value is box V2 on the proxy
address. version if this works this
means that on the proxy address which
originally pointed to boxy one it's now
pointing to box box V2 we could also do
box V2 proxy dot set value or what do we
call it box V2 set number set number
number we say like seven or something
right and we could say assert equal
7A comma box V2 roxy. getet number like
this and we could even add another
function test proxy starts at as box V1
right public we could say vm. expect
revert with calling th this function
because set number doesn't exist on box
V1 so we try calling set number on the
proxy it should fail so let's run these
one at a time Forge test- M this should
indeed
revert great and then let's test this
and this should indeed upgrade Forge
test t-m test upgrades oh we failed
let's get some granular detail here- VB
left is seven right is zero set number
get number oops we forgot to actually
add set number in here so let's do
number equals underscore number whoops
now let's run that
test and boom it passes we have
successfully upgraded our box this is
very exciting
so let wrap this up I know we went
through this really quickly I want you
to ask a ton of questions about this
process ton of questions about this ask
them in the discussion ask them on
Twitter ask them to your buddies
whatever you want to do we just learned
about upgrades like I said I highly
recommend you do not default to this and
you do not let protocols that have
upgradeable smart contracts keep this
centralization Vector because it is if
you have an upgradeable smart contract
that means that a group could change the
logic of that code at some point or be
coerced to change the logic Etc but this
is an incredibly powerful primitive to
know about especially delegate call
we're almost done push this up to GitHub
add this to your portfolio take a break
congratulations
I'm going to go ahead and deploy this to
a test now just to show you what it will
look like on ether scan feel free to
just watch sit back relax you've done a
ton of amazing work to make my life
easier I'm going to borrow a make file
and just kind of tweak it let's change
let's just delete all this stuff deploy
it's going to be deploy box box upgrade
P script upgrade box
box make deploy args equal Das Das
Network
aoia all right so I ran the deploy we
deployed two contracts let's grab this
one firsta ether scan let me show you
what this looks like looks like this one
didn't quite verify correctly I could go
ahead and manually verify it if I wanted
but let's look at the second one looks
like neither one of these actually
verified correctly but that's fine I can
go to the my broadcast see which one is
which we can go to both these runs let's
see the first one okay we did create box
and so this is going to be the address
of box V1 so I copy this paste it in
here I can go ahead and verify this
manually later but I know that this is
my box V one we'll go back to broadcast
click this one scroll down uh this one
doesn't have a contract name but I know
that this one is going to be my proxy
address so I go to your browser let's
open keep them both up paste it in here
this one didn't get verified correctly
either but that's okay so this one's our
box this one's our proxy and this is the
one that's more important right this
proxy is pointing to this implementation
okay now we're going to call our upgrade
we're just doing make deploy make
upgrade like this oh and I
needl since I'm using Foundry devops I
need ffi equals true remember only do
that if you are confident about what
you're running do make
upgrade so this actually is a bit of a
bug with this script that I just found
out I do need to update my run latest to
ERC
1967 proxy I either need to upgrade my
script to look for null or I need to
upgrade Foundry to add this for the
proxy name so I'm just going to manually
add this in we're going to hit clear
we're going to rerun it look like that
is a bit of a bug sorry about that but
now that it knows that this is the ERC
1967 proxy we're actually running this
script now to upgrade the box and the
first thing that we're going to do is
we're going to deploy our box V2 which
for some reason is actually verifying
looks like something was just wrong with
the first time I did it but all good if
I paste this in we now have a box V2
being created and it looks like it's
trying to verify right now oh and looks
this one actually did verify so if we
refresh this one so it looks like our
box V2 is here we place this in it looks
like this one did actually verify which
is great box Fe 2 now if we go back to
our proxy we hit refresh we can see we
called an upgrade two on this contract
now whenever we call functions on this
it'll actually point to box V2 right so
we can even grab the contract address
we'll use a little cast send paste this
in here we'll call get number like this
or actually excuse me cast call cast
call and of course Das Das RPC URL seia
RPC URL this we can see that it starts
off at zero that number starts off as
zero then we can call well we can do
send instead set number takes a u into
256 we'll set the number
277 RPC D- private key private key
remember you're going to need to do
source. to do this we'll send a
transaction to actually update update
this and we're calling the proxy right
we're not calling this box V2 address
we're calling it right on the proxy then
if I go up up and we do call again we're
now going to get 4D for the binary and
if we do cast
d-2 base paste DEC for decimal we
convert this hex to decimal and we see
we get 77 back so we have successfully
deployed and worked with a proxy this is
so awesome take a break we got two more
you're so close you're almost done we
got two more scrolling all the way down
founder governance another quick one and
then introduction to security and then
I'm going to send you on your way to
make the future of Finance the future of
smart contracts better place you're so
close take a break get ice cream post on
Twitter I will see you
soon all right welcome back we're almost
there everybody let's scroll on down to
lesson 14
we are now on Foundry dowg governance
and of course all the code that we're
going to be working with is in this
GitHub repo so if you want to follow
along feel free to go here now there's a
ton of conceptual stuff to go over
before we get into this but basically
we're going to learn everything about
what a dow is what they're for and how
to use them and as we're going along and
as we're learning about Dows I want you
to take some time to read these two
articles they're both from vitalic and
they address a lot of the main concerns
with dows one of them are that Dows are
not corporations where decentralization
in autonomous organization matters and
this article came out in response to a
lot of people having really hard
coordinating doubts because since
they're decentralized autonomous
organizations and it can be difficult
sometimes to coordinate with so many
people and so many heads so this article
is really interesting where Dows work
really well and sometimes where they
don't and this one is one of my favorite
articles about plutocracy plutocracy is
is government by the wealthy this is
something that obviously sounds bad and
something we want to avoid in this new
system that we're creating if we default
to typical erc20 based governance we're
going to run into a lot of issues
outlined in this post if that doesn't
make sense to you right now don't worry
it will and we'll come back to this in a
little bit but first let's learn about
Dows what Dows look like and how they
actually work especially as developers
so I made some videos about this in the
past let's go ahead and watch those and
we'll come
back now before we learn how to code a
dow we should learn what a DA is and
again I've already made a video that I
put a lot of work into so we're going to
watch what a DA is from a high level
first then we're going to learn how to
code a
da now Dows or decentralized autonomous
organizations is a bit of an overload of
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 Dow
because the miners could 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 Dow is just a
buzzword so they just slap the name
really onto any organization so that
they can get some clout and this makes
her sad
Patrick and this is enough to be
confused with the da which was an
implementation of a DA 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 it 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 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 of the protocol
that's setting the precedent for all
other Dows 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
going to have a full codal 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 that 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 often times they're going to want to
do a lot of new things maybe they want
to add a new token to allow borrowing
and lending maybe they're going to want
to change some of the apy parameters
maybe they're going to want to block
certain coins there's a lot of different
things that they might want to do so
that's where we're going to go ahead to
governance this is where you can find a
user interface for a 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 that this
protocol has actually been making to
improve and let's look at one of these
proposals that currently actually in
process so if we click on the proposal
we can 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 can
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 a 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 parameters 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 say is that these
contracts likely have access controls
where only the owner of these contracts
can actually call these two functions
and the owner of these two contracts is
likely going to be this governance sty
and values to zero just means that we're
not going to send any eth along with
these transactions once a proposal has
been created after a short delay it
becomes active and this is when people
can actually start voting on them this
delay between a proposal and an active
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 if we click on
this transaction again and we go to the
compound governance contract and we
scroll down to contract right as proxy
we can actually see the exact function
that the people called 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 function 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 techsavvy so you can vote right
through this app. compound. Finance or
you can just send the transaction
yourself once all those votes happen it
reaches this cued stage now what does
cued 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 cued function
and it only can be called if a vote
passes and it says okay that proposal 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 82
so this is going to be a full example of
the life cycle of a proposal going
through this process now there are a
couple that even failed whole punch of
you 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
often times 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 like them or don't like them
often times a discourse is one of the
main places that people are going to
argue for why something is good or why
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 could 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 Dows on how to vote now
you know now that we know what a dow
looks like let's talk about the 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 Dows because sometimes they do
need to update and change to keep with
the times not all protocols need to have
a dow but those that do need to have a
dow need a way for participants to
engage this is one of the most important
questions to ask and to tell your
communities how do I participate how do
I engage in this D
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 erc20 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 hooray now this actually might be
the right approach for certain Dows but
it also runs the risk of actually being
less Fair because when you tokenize the
voting power you're essentially auction
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 nfts 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 Dow and Community
setup is going to look like now the next
one we're going to talk about is skin in
the game now vitalic 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 asked you get
punished for making evil or bad
decisions for your Dow and your protocol
I like this mentality because even if
you buy a ton of tokens and decide to be
evil with it you can be held accountable
for your bad decisions now the hardest
part about this though is going to be
how do we decide as a community what is
a 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 used the protocol and even
if they had a thousand 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 thousands 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 chain link
integration because proof of personhood
is basically just offchain data that can
be delivered on chain and that's exactly
where chain link shines now as you can
see all 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 a person who or approve of
participation might just be the exact
same as kind of the regular 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 erc20 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 TW space now
when it comes to implementation of the
voting I put them into two categories
onchain voting and offchain voting
onchain voting is exactly what we saw
with compound there's a smart contract
on chain you're a voter you call some
function called vote with your metamask
your Ledger or whatever send a
transaction and boom you voted congrats
you can wear your little thicker Now
call that function and you send a
transaction you send a transaction H
what do transactions use that are kind
of annoying and kind of costly uh oh
that that's right gas imagine you have
10,000 people in your community and it
costs $100 to vote per person you are
now costing your community $1 million
anytime you want to change anything this
is obviously insane and not very
sustainable for your community the 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 C where they use some
random sampling to do some quadratic
voting to help reduce cost while
increasing civil resistance you want to
learn more about that one too be sure to
read about it in the description so
onchain voting is the simplest one here
but let's talk about offchain voting how
could you possibly vote off chain in a
decentralized context relax relax you
can vote off chain and still have it be
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
deliver 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 these signed
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
secure and better and blah blah blah
dying for it this is your call to action
go build this thing this offchain 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 decentral of your application so
don't do that and if you made it to this
point in the video give yourself a
little path in the back you're doing
fantastic learning is fantastic like I
said I have a video coming out after
this one that's going to show you end to
endend 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 Dows Dow
stack
Aragon just kidding this is Aragon
colony and DOW house are all
Alternatives that can actually help you
with the Ops side of running a and
building a doubt however if you want
more granular control and you don't want
to have to pay any of 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 dow
and actually performing that execution
users can vote through this protocol
with their actual tokens those
transaction gets stored in ipfs but none
of it actually gets executed unless the
Dow chooses to so this can be a great
way to get the feel for what your Dow
wants to do
and optionally you can send the
transactions and execute the votes as
well I highly recommend checking out
zodiac which is a suite of Dow based
tools for you to implement into your
Dows as well tally is another one of
these uis that allow people to see and
actually vote and interact with these
smart contracts through user interface
for those of you who don't know about
nosis safe you absolutely should nosis
safe is a multisig wallet and the reason
that I put this on the list even though
it's adds kind of this centrality
component is that most Dows 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
thousands of people to wait for a vote
and in the beginning any protocol is
going to be centralized to some degree
anyways using a multisig where voting
happens through only a few key members
can be good in the beginning for your
Dows 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 open Zepp
and contracts we love open up in
contracts these are the contracts that
we're going to be basing our Dow codal
on all right so that's all the tools
that's the architecture one more thing
before I let you go legality the future
of dows is interesting for all these
reasons we just talked about but
especially on a legal front does it make
sense for a DA to live by the same
regulation as another company how could
you even force a DA to do something
you'd have to force them to all vote a
certain way if the government tells you
to it's it's a little gray it's hard to
nail down who to even keep accountable
for these Dows in the United States at
least you can actually form your own Dow
and have it legally recognized in the
state of Wyoming this is something I
want to do someone just have to see what
happens there w 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 it's time to build
[Music]
baby now we are still going to build a
dow from scratch but we do have our
special guest Juliet from the Aragon
team to go over a no code solution that
you can use as an alternative hello
everyone uh my name is Julia chier I'm
the developer advocate for Aragon and
today we'll be building a dow without
using a single line of code let's go so
to do that I'll be using Aragon as my
Dow framework and to give you a bit of a
better overview as to how Aragon
architecture is set up um Dows are
essentially composed of a smart contract
which is the one that contains all of
the assets that the organization manages
um so it acts as a treasury and then all
of the other functionality for Dows is
really enabled through plugins and we'll
see how this uh how this is represented
itself throughout the process that we'll
build so we go into ab. ar.org we go
into creating a da um first we'll see
the process uh that we'll be taking in
order to do that and then we can get
right into it so firstly we need to
choose the blockchain which our Dow will
be deployed in um I'll choose orally for
the sake of Simplicity and playing with
testnet um then we get to describe our
Dow so we can have a developer Dow um
which can be something like this we can
set up our ens we can set up our logo a
description a dow for developers um and
we can add in additional oh we can add
in additional links um as well that
represent the
organization then we'll go into defining
the membership this is the what will
Define who is able to participate in the
governance of these assets um and
currently our app supports two different
types of uh membership definitions and
governance mechanisms one of them is
token holders which enables holders of
specific tokens to vote in the
organization and then we have multi-c
members which establishes a specific
Quorum that needs to be met in order for
a proposal to go through now to give you
some context both of these on the back
are plugins um which essentially extend
the functionality of what Dows can do
through enable them this specific uh
decisionmaking process in order to
manage the assets of the
organization however keep in mind this
can be anything and plugins can also
work to move uh funds they can be
treasury management mechanisms they can
be different types of coordination
Styles um and so really the
possibilities are endless when we speak
about uh customizing these types of
onchain
organizations then we're going to create
a token um this this can be something
like developer token we can call it
something like DVP um I can set up say a
th000 tokens for myself or add any other
address that I want to transfer uh some
funds to um then I establish what is the
minimum amount of tokens that someone
needs in order to create a proposal I'll
say 10 uh I could also just say like
allow any wet out there to vote um and
create proposals at which point uh there
is a risk of proposal spam so keeping
that in mind I'll just say that we need
um say 10 tokens in order to create a
proposal then we'll go through our
governance settings so what is the
minimum support threshold needed the
minimum participation needed for a
proposal to be valid how much times the
minimum duration the uh proposal needs
to be out there in order for it to uh be
counting the votes and then we can say
that we don't want early execution which
means that we wait for the entire time
of the duration of the proposal as well
as uh not enable people to change their
vote after they' voted I can also vote
yes to that and then the last step is
simply revising um those parameters that
have set so out of everything that we've
defined only the blockchain selection is
the one thing that we cannot change
later on since that's the blockchain
through which we're deploying our Dow
but really everything else um we can
change it later later on with a proposal
vote um and so the idea here is to
really enable these types of immutable
organizations to mutate and evolve with
time um so I'll sign in my proposal I go
back into my deployment um and so at
this stage what's happening on the back
is that Aragon is deploying this Dow
construct instance with uh the specific
plugins defined installed as we
determined throughing through the Dow
creation
process so once that's done we'll can
come into the dashboard of our Dow we'll
be able to see who is a member of our
organiz ation how many tokens are hold I
can create proposals I can make deposits
um and so this is where we'll be
managing the organization um keep in
mind that this is a a pretty um default
template that Aragon has set up as our
initial drafts we have a lot of
iterations coming in terms of the type
of plugins you'll be able to use the
different types of uh decision-making
strategies you'll be able to implement
and so the customizability really is
endless thank you very much
all right welcome back you now know a
ton about Dows and how they work and
we're are going to go over one of these
plutocracy Dows right one of these erc20
based Dows and how to build one yourself
from scratch however I want to challenge
you to not default to this a lot of
protocols default to okay I'm just going
to launch a governance token we're going
to be good to go and then a couple years
down the line they realized that that
was a horrible decision and they have a
hard time scaling their model they run
into issues where they can't separate
speculation on the token from the
utility of the token and there's a ton
of issues with that model so anybody
who's watching this if I catch you
launching a DA in the future and you do
not have a foolproof white paper as to
why you need a governance token I will
be very disappointed in you I know that
doesn't really matter to you but I will
be very disappointed in you and you will
make me sad so please don't make me sad
but we are going to learn this
methodology and how to do it so let's
jump in because guess what we only have
two lessons left and then you're ready
to go on your way all right you know
where to begin mkd boundary Dow f23 code
boundary Dow f23 or file open let's pull
up our vs code at this time you should
be pros at this part Forge in it
I don't want these three files boom boom
boom goodbye boom so let's do a little
read me and let's talk about what we're
going to do so one we are going to have
we're going to have a contract
controlled by a dow 100% controlled by a
dow every transaction that the Dow wants
to send has to be voted on and we will
use erc20 tokens for voting which like I
said this is kind of the default model
that the industry is currently using but
but I'm telling you it's not a great
model please research better ones and
make web 3 better but all right and this
is the model used by pretty much every
Dow at the moment compound Unis swap a
etc so I'm showing you the most popular
model but yes I'm telling you please
don't default to this please don't
always use this but let's start with
this first one we're going to have a
contract controlled by the Dow so let's
create a minimal contract that we can
vote on stuff for and we're going to do
something pretty similar to what we've
already done we're going to do SRC new
file we're going to make a box. soul
real similar to what we did before right
spdx license identifier MIT pragma
solidity
0.818 little carrot contract box let's
close this like this like we said we're
going to make this ownable it's going to
be controlled by the Dow so we're going
to do a little open Zeppelin work Forge
install open Zeppelin slash open
Zeppelin
contracts D- no- commit and since we're
doing this we're going to need to open
up our
founder. paste this in here uh I copied
this from a previous project opens up
on/ contracts lib opens up and blah blah
blah now since we have that we can do
import ownable from atop Zeppelin SLC
contracts SL
accessable Doo and our box is going to
be ownable and the Dow is going to own
it tracks all right cool going to be
real minimal contract un 256 private
number this obviously should be like
sore number right uh event number
changed looks like that function
store un 256 new value or new number
public this will be our only owner
function which this store function will
be only ownable will be only callable by
our Dow we'll say number equals new
number we'll even
emit number changed with the new number
this is sore number and then we'll do a
function read number or get number get
number public or external view returns
un
256 return s number and that's it that's
the whole contract boom that's it this
is kind of looks like box kind of looks
like symbol storage it's kind of an
amalgamation of both but this is it cool
we'll make sure it actually
compiles with Forge build make sure it
compiles looks like it's compiling okay
cool awesome so we have our box what's
next well every transaction that the Dow
wants to send has to be voted on we will
use erc20 tokens for voting so let's
create our our voting token here
new file go token. soul and this is
going to be our voting token now we
could implement this from scratch but
again what we can do for this is
actually just come to the contracts
wizard of open Zeppelin go to erc20 and
select votes and literally just copy
this to clipboard and paste it in or
copy this ourselves and paste it in here
this is going to be our voting token
that's it so let me talk to you about
what we just copy pasted and we're going
to update this to be named Imports
because named Imports are awesome this
draft erc20 permit we're just going to
import this erc20 permit and then erc20
votes so I'm just going to talk you
through what this is actually doing so
it's a regular erc20 token exactly as
we've seen but has these two kind of
extensions let me toggle the word wrap
and we have the opens up and contracts
installed okay pool so I'm going to
control command click into them again
you can open up the file viewer and you
know type the actual name of the
contract if you want erc20 permit what
does this actually do this allows
approvals to be made via signatures
basically you can sign a transaction
without sending it and let somebody else
send the transaction that's it it's
based off of this EIP we don't really
need to understand this too much but if
you're interested I would definitely
recommend recommend looking into it the
more important one is this erc20 votes
contract and like I said it's based off
of this compound voting in delegating it
does a lot of important things here
number one it keeps a history of each
account's voting power keeps snapshots
of them now this is important because
when you start a vote you want to use a
historical snapshot you don't want to
use the current balance of everyone's
tokens at the moment somebody could do
something called a flash loan which is
something we didn't talk about
but they basically can use this defi
thing to get a ton of tokens for a very
very short period of time and crash the
vote so we use a historical value and
the way to get that historical value is
to keep checkpoints of people's values
we can also use a delegate function on
our tokens sometimes we trust a person
more than we trust our understanding of
a topic so we can actually hold on to
our tokens still that tokens are still
ours but we can delegate the voting
rights of our tokens to somebody else
these are some of the two main features
that this token has if we scroll down we
can see we have this checkpoints
function which gets checkpoints and
there's this checkpoints mapping address
to checkpoint we can see the checkpoint
object says from block and then votes
and if we scroll down there's this Apon
token transfer function which anytime
you transfer a token this gets called it
ends up calling this move voting power
which does a whole bunch of of stuff in
here but the main things are it calls
this right checkpoint function so
anytime you move tokens by delegating
tokens or transferring your tokens you
write a new checkpoint right so you say
hey at this block this was people's
voting power and you don't do it for
every single block right you don't
calculate someone's checkpoint in every
single block you only calculate the
updates in the checkpoints so if we
scroll to the top for example says from
block this would be like you know let's
say you minted let's say you bought a
token at block and then you sold it at
block 25 this would just have two
checkpoints block five and block 25
that's it it adds some gas for every
single transfer you make but it allows
us to do these sophisticated voting
methodologies so and that's really it
other than that it's a basic erc20 token
just has these checkpoints has these
additional delegate functions as well
and that's really it and cool like
that's it our governance token is done
that's all you need to do
what else well guess what the open
Zeppelin wizard can help us with even
more CU what do we need next well we
need a doubt right we need a system we
need a contract to actually manage these
votes right and guess what the wizard
has one called Governor we can go
through this and update settings so we
can update the name if we want I'm going
to leave it as my Governor a voting
delay which is anytime somebody proposes
something it says okay we're going to
wait at least one block for the voting
to start maybe you do something like 100
blocks you know it depends on your chain
the voting period for how long the
voting will actually last a proposal
threshold where the minimum number of
votes an account must have to create a
proposal most of the time this is kept
zero a quarum percentage percentage of
tokens that need to vote in order for
something to pass if there's 100 tokens
in circulation at least four tokens need
to vote for it to pass make these
settings updatable or not this is this
is to make the contracts fully
compatible with Bravo which is the
compound implementation the type of
votes you can have erc20 votes you can
have ERC 721 votes which is nft or
comple a time lock controller anytime a
vote passes give users some amount of
time to leave the Dow before it's
implemented and then upgradeability we
probably want to avoid this as much as
we can because like I said before
security and with that I'm just going to
leave all the settings to be default we
can literally copy this into our
contract and not really have to do
anything so new file my
myor do Soul B this in and let's talk
about what is going on in this contract
what are these different functions so
first off let's update this to named
Imports and we also need to import IA
votes this is going to come from
Governor votes we're going to need to
import this time lock controller which
is going to come from this Governor time
lock and then we're going have to import
I Governor which is going to come from
governor and that's it so just update
these to named Imports and we're good to
go so let's walk through what this is
doing so my Governor is governor
governor settings Governor County blah
blah blah all this stuff I'm going to
give you the high level overview of what
all of these does this Governor contract
if we command click into it or control
click into it
the core of the governance system this
Governor contract we control click into
it it brings us to Governor votes but
let's go to the governor contract this
is kind of the main contract here and
how it works is it keeps this mapping
basically of proposals right each one of
these proposals have a vote start a vote
ended and then an executed or canceled
and that's really it every single time
somebody submits a proposal this mapping
gets updated and if we scroll down we
have this state function this is the
function that tells us if a proposal has
passed based off of this mapping of
proposals if it's executed if it's
cancelled what's the deadline has a quum
been reached Etc this is what most user
interfaces are going to read to see if a
vote has passed or not keep scrolling
down we can see a proposal snapshot
which is basically getting the deadline
when the proposal is due we can see some
stuff like a proposal threshold and then
a whole bunch of unimplemented functions
like Quorum reached vote success get
votes Etc and this is because this
Governor function is this abstract
contract meant to be extended on and
implemented with Governor votes is one
of these extensions where it says okay
erc20 token voting that's the way we're
going to do votes and this has a
function like get votes which reads the
token voting weight from the tokens
built-in snapshot mechanism to get how
often somebody can vote
uh and that type of thing now in our
governor one of the main functions that
we has is going to be propose and these
are going to be the variables it takes
in targets is just going to be like a
list of different contracts to make a
transaction to values is going to be a
list of values to send with each Target
call data now that we know all about
call data is going to be the byes data
that we're going to send with each
transaction and then just a description
right so for example this might be like
0 x0 blah blah blah some address here
and if it's just one it would just be
like you know one address here it's two
you know it might be like this Etc
values might be like you know something
like zero meaning the transaction we
send to this address we're just going to
pass zero value you know if there's two
transactions with two addresses like 0x
blah blah blah something or other you
know we could have two values right call
datas are going to be
the call data for sending to Here and
Now that we know about function
selectors function signatures and Etc we
know that with this this call datas
section allows us to make allows us to
call any function and really send any
data to this target that we want so it
makes it completely modular and then
some description right and this is
really just for the internal state of
the governor you know it might be
something like proposal
to send money to Dawn right and so if
this was like one two3 this is Don's
address cool we've just made a proposal
to send money to Don right if this was
you know like like the link token
contract for example we could have this
be zero we could find the function
selector for transfer we could be like
transfer function
selector and encode you know and then
encode all the data for the for
transferring some link we could then say
proposal to send link to Chris or
whatever we wanted to do here right so
this is how we can propose any
transaction for our Dow to do and send
proposals so really really important
function here and if we go through it we
can see it hashes the proposal it does a
whole bunch of checks in here it adds
our proposals to that proposals mapping
it gets a deadline picks a snapshot for
votes and everything emits a log and
importantly it returns a proposal ID The
Proposal ID is a hash of all the inputs
so that this proposal ID will be unique
then we have an uh another important
function called execute and all of the
inputs you can see are the same as the
inputs up here right the only difference
is it's saying a bytes 32 description
hash this is just going to be the
encrypt description from up here and
it's this function that gets called o
excuse me and it's this function this ex
Cube function that gets called once a
proposal has passed to actually send
that proposal right so here it first
reverse engineers and gets the proposal
ID from all those inputs which should be
the same as what was proposed and then
says okay let's get the status of that
proposal State based off of how the
votes go and if it's good to go then go
ahead and execute it right so we do some
execute stuff here the execute internal
function actually sends it with some
familiar stuff here right we're doing
targets. call values call datas Etc this
is this lowlevel functionality here it's
got some before execute stuff some after
Execute stuff we don't really need to
worry too much about that we can vote to
cancel transactions we can get votes and
then finally another incredibly
important function is going to be cast
vote and there's some derivatives of
this like cast vote with reason reason
cast vote with reason and pams cast vote
by Sig whole bunch of different ways to
cast vote but the main thing this cast
vote thing does is it calls this
internal cast vote function which calls
this internal cast vote function and
this is the actual function it calls so
it gets the proposal based off the
proposal ID it gets the weight of your
vote based off of the snapshot of the
tokens that you hold calls this internal
count vote function which basically just
updates some internal State variables
to add your votes to them and then
admits an event that's it right and
updates your vote based off of whatever
your weight is which in the system that
we're using the more tokens you have the
more you your vote counts and those are
the main functions here so hopefully
that made sense I know I kind of went
through that a little bit quickly but
that's kind of the basics of the
governor now Governor is the main
contract Governor settings is just an
extension to add stuff like voting delay
voting period voting threshold Etc
Governor counting simple is also an
extension uh which is just an extension
for a very simple counting mechanism
this extension allows our votes to be
against for and abstain and so we have
this proposal vote for each proposal
they get this struck and this is where
those votes get counted we have against
votes for votes abstain votes and then a
mapping of who has actually voted
Governor votes just implements this get
votes contract and Governor votes quorum
fraction this is just an extension or
token weight extraction and then we have
this important time lock controller
function every single Dow should 100%
always have this Governor time lock
control using this model means the
proposal will be operated by the time
lock controller and not the governor
thus the assets and permissions must be
attached to the time lock controller so
we're actually going to deploy a
contract called time lock that's going
to own the governor anytime our governor
contract votes on something and says
okay this vote has passed let's executed
the time lock controller will say okay
cool but you have to wait X days or
something you can't execute without some
delay and it's really important to have
this because if a dow votes on something
that a lot of people in the Dow hate it
gives them time to get out of the Dow
before they get steamr if you will so
really important to have this and that's
pretty much it so we have some uh voting
delay voting period Quorum State propose
we have all these uh which are just
calling super right that's pretty much
it and that's all we really need for our
governor contract right we have this
Constructor which takes the token that
we vote on as an input parameter and the
timelock controller that's going to
block us from block us from executing
proposals right away time lock
controller contract is going to own our
governor controller contract and that's
actually the last contract we need to
create a Time lock. Soul so this is the
only one we have to build ourselves but
we actually barely have to build it
ourselves SPX license identifier pragma
solidity 0.
8.18 carat contract time lock and we we
barely have to implement this because
open Zeppelin already has one of these
so we just say our time lock is
time lock controller and then we can
just import it import at open Zeppelin
SL contracts SLG governance time lock
controller. let's import it like this
from I spell that right time lock
controller okay that's more right
governance governance contracts
contracts open Zeppelin open Zeppelin
time lock cont controller time lock
controller okay cool that looks good now
we'll just do a
Constructor and we'll pass some
parameters in here we'll say U 256 Min
delay which we'll put a little comma
here this is going to be the Min delay
is how long you have to wait before
executing we're going to have to pass
and we can find all of these in the
Constructor of
this m delay proposers executors and
admin right this is the Constructor of
the time lock controller so we're going
to need to have this be the Constructor
of hours as well M delay we're going to
need
proposers is the list of addresses that
can propose so we need an initial list
of proposers we're going to update this
so that it can be anybody and then xeq
tors is the list of addresses that can
execute execute we're going to update
this as well so that anytime a vote pass
is anybody can execute it so M
delay address array memory proposers
address array memory
xter boom and since this time lock
controller parent class has a
Constructor I'm tle the word WP we going
to put those in here and it's just
exactly the Constructor parameters we
created in delay proposers
xors computers
and then we also have to give it an
admin which we're just going to say is
message. sender for now and we need to
give it an admin at first so that we can
eventually move the Tim lock controllers
admin so that only the Dow can do
anything with the time lock controller
and you'll see us do that in a minute
but those are all the contracts that we
need that's it we've written the
contracts we're done like I said I'm
going to do this kind of quickly I do
recommend that after this you go back
and you really try to understand what's
going on here because I I know I'm kind
of doing this quickly but now we're
going to write a test to show it
actually work and that's it I'm not
going to write a deploy script here uh
if you want to go back and you want to
write a deploy script that's great but
I'm just going to write a test to show
you basically endtoend exactly the
process that happens in one of these
Dows so let's go ahead let's write the
test my Governor test.
T.O and the other reason that I'm going
a little bit quicker here is because at
this point you understand a lot of this
right and you know and should know how
to go back and flush this repo out right
with scripts with tests unit tests fuzz
tests Etc and you know how to make this
project badass and I'm just I just want
to give you the new stuff right teach
you the new stuff for here teach you
about the Dow stuff so let's write some
test just so that we can really see this
process work end to end and we're just
going to have one giant test just to
show the whole process so you know the
drill pragma solidity here contract my
Governor test is test you already know
we're going to import test from Forge
STD test.
soul and we're going to need a function
function setup it's going to be Public
public of course and we're going to need
a whole bunch of stuff in here function
setup oops what are we going to need
well we're probably going to need at
least these four contracts so let's go
ahead and import them
import
myga nor
from as Maga veror do Soul import box
from Sr cbox
Doo import time lock from here hit up
co-pilot taking over a little bit import
gov token okay it failed it almost took
over here from SRC go
token. s oh is that not what it's called
ah sorry let's change this name to gov
token like this it's good okay so now
let's actually deploy some of these so
our gov token say actually let's create
each one of these my governor
governor box box time lock time lock go
token go token cool first thing that
we're going to need is our gov token
equals new gov token and I know this
doesn't take any input parameters but we
should give our gov token we should
actually mint some of these this gov
token doesn't have a mint function so
I'm just going to create a mint function
but in your system maybe you just have
the Constructor mint to the message.
sender I'm just going to go ahead and
add this function mint address 2 256
amount
make this public um this probably isn't
what you want your token to have you
probably don't want anyone to be able to
Mint any tokens but this is what I'm
going to do mint to amount that's it
there's an internal function called mint
that we're going to call so I'm just
going to say I'm going to Mint to some
user I'm going say
address public user equals make address
this is going to be our user here do
this capitals user like this say gov
token. mint user and we'll do un 256
public
constants yeah initial Supply equals 100
ether we're going to Mint ourselves 100
ether boom all right cool now something
that you'll often forget is that just
because you minted tokens doesn't mean
you've have any voting power so we want
to run gov token. deligate delegate
function and we're just going to
delegate to ourselves so we're saying
hey we now have 100 tokens we're going
to delegate those 100 tokens voting
power to ourselves you could delegate it
to somebody else if you wanted to but
for our test we're going to do it for us
and we need to actually do vm. start
prank user because only the user can
delegate tokens to themselves and we
might as well stay as the user for these
next ones just that we can start off as
owners of the contracts we're about to
do so now that we have our governance
token next what we want to deploy is our
time lock because in order to deploy our
governor we're going to need both the
gov token and the time lock so we have
the gov token let's actually create the
time lock now so we're going to say time
lock equals new time lock and what does
this take M delay posers executors so
let's create those going to scroll up
we're going to make un 256 public
constant Min delay delay equals
360 0 which is going to be 1 hour this
is the delay after a vote passes right
so this is the time locks M delay nobody
can actually execute a past proposal
until an hour goes by boom we'll do M
delay what else do we need we need the
proposers for stuff and we'll just make
this at the top address array Pro
proposers address array
executors and we'll just leave it blank
because we don't care proposers
executors and this is actually how with
the time lock controller you say anyone
can propose and anybody can execute you
just leave these arrays blank cool looks
good okay now that we have that we
finally can deploy our governor contract
CU we have these two so we'll say my or
what do we call it do we just call it
Governor yep governor
equals new my
Governor go
token time lock like that now we have to
Grant some roles so the time lock
actually starts with some default roles
and we need to Grant Governor a whole
bunch of roles and then we need to
remove ourselves as the admin of the
time lock right we don't want single
centralized entity to have power over it
so first we got to get some of the roles
so we're going to say byes 32 proposer
roll and the way that the time lock has
roles is it hashes the names of stuff so
we'll say proposer rooll equals time
lock
proposer Ro there's this function on the
time lock called proposer role uh and
we're going to allow this proposal Ro to
be just the governor so only the
governor can propose stuff to the time
lock say bytes 32 xq tour Q
rooll is going to be Tim lock. executor
ro we're going to give this to anybody
by setting it to the zero address and
then byes 32 admin roll right now that's
us time lock dot nope it's going to be
time lock admin roll and so like I said
these are just kind of the um these are
hashes in the time lock and we just need
to update these we're going to say time
lock time lock dot it has a grant roll
function we're going to Grant this
propos propos a role to the governor so
only the governor can actually propose
stuff to the time lock or say time lock.
Grant roll xq rooll to address zero
which means anybody can execute a past
proposal and then time lock. revoke
rooll revoke the admin role from user so
the user will no longer be the admin and
then we'll say bm. stop prank this then
we just need a new box box equals new
box and now we're going to transfer
ownership of the box to the time lock
the time lock is owned by the Dow so
we're going to say box. transfer
ownership to address time lock so
transfer ownership a common
misconception here is oh shouldn't we
transfer to the Dow no we should
transfer to to the time lock so the time
lock owns the Dow so the time lock owns
the Dow and the Dow owns the time lock
it's kind of this weird two-way
relationship but it's the time lock that
gets the ultimate say on where stuff
goes so we transfer it to the down and
so we can even do a quick test function
test can't update box
with governance public oops public we'll
say vm. expect revert box. store one
like this since our
box has a store function that's only
owner the owner is now the time lock SL
the Dow oops if I try to run this test
right Forge test DM paste this in this
should pass
because you can't update the Box unless
it's through
governance and this will also be our
sanity check to make sure our uh all of
our code is compos which it is okay
great let's keep going so now let's make
a giant function function test
governance updates box public and this
will show you the exact process from a
code standpoint of how one of these Dows
actually work so first let's get started
un 256 value to store is going to be 888
boom so we're going to update our box
right this box here we're going to
update this new number to 888 okay let's
come up with a proposal right remember
if we show up my Governor we click into
governor in uh and we click into
Governor function
propose right here first thing we need
to do to kick off anything is propose
something hey we are going to propose
that box updates the stored value to 888
so we're going to need to have all this
stuff in here Target values call datas
descriptions so let's start with
description because that's simplest
right string memory D description equals
store one in box that's it that's our
description pretty easy right what else
do we need okay we're going to need some
call datas oh well we know what call
datas are that's that function selector
and all that other stuff that we learned
in that nft one right so we can say
bytes memory
so if you go back to The Foundry nft f23
we go to SRC subl lesson we can remember
oh wait we can we can call anything that
we want right and all we got to do is
encode stuff right like ai. encode with
signature or encode with selector or
whatever we want to do here so we can
actually say oh okay so this allows us
to make any function call that we want
so we'll encode it by saying encode
function call equals we want to well
what do we want to call well we want to
call this store function ah so we know
the function signature of store is going
to be store unit 256 so we can say ai.
encode with signature we're going to
call the store function like this and
then what else do we need well we need
the value like this and that's it cool
let me toggle the word rep here cool so
we have the encoded function call so we
have the call datas that we need to call
in okay great what do we need next and
we're going kind of in this reverse
order values uh well we don't really
care to send any value right so let's
even just go to the top here we'll make
a un 256 array values it'll start off as
blank we'll just say values. push Z cool
we'll just have it be empty we're not
going to send any eth and then the final
thing we need is targets so we want to
call this on the box contract right oh
and actually call datas is also an array
so we're going to do a couple things
we're going to do
values all
datas and targets targets right so the
call datas is a un 256 no call datas is
a bytes array targets is an address
array so we're going to say values. push
zero call datas do push that encoded
function call that we just made and then
targets. push the address of the box
right so now we have all that now we can
finally call our proposed function and
what does this proposed function return
returns this un 256 proposal ID and we
need that to keep going so first thing
we're going to do is propose to the Dow
propose to the Dow so say you and 256
six proposal ID equals
governor. propose the targets values
call datas and
description boom and we got our proposal
ID just like that now we can even view
the state of this proposal now right
because we can call that state function
so we can do even like a little
console.log
proposal state uh we can call a state
function like this actually get a
copilot has this right except for we
want to turn this into the U 256 version
so we'll say un
256 governor. State proposal ID like
this and if we look up function State
we'll find this there's like a million
of these in our governor. soul it
returns this proposal state which if we
control or command click on that we can
see all the different states that can
have pending active canel defeated
succeeded cued expired executed so
pending obviously is going to be zero
active is obviously going to be one
canceled is going to be two Etc right
and of course we're going to need to
import console from test Okay cool so
this should return what should return
pending it should return a zero and it
hasn't started yet it's not active
because there's a delay right so we want
want to actually update our fake
blockchain to move past that so we'll
just do vm. warp we'll use these cheat
codes remember lock. timestamp plus and
then we'll just do voting delay you went
six public constant voting delay equals
one this is how many blocks till a vote
is active right and from our wizard we
just left this as one oh go back to
Governor we just left our voting delay
as one so this voting delay is one so uh
plus voting delay and then actually
we'll do voting delay plus one and then
we'll also do roll to increase the time
as well bm. warp EXC going me to
increase the number block.
number plus voting delay plus one now if
you view the state it should be active
so now it'll be active cool two after a
proposal starts because we've proposed
enough time has passed we now can
actually start voting on something so
I'm going to do this cast vote function
but I'm going to do cast vote with
reason because I like cast voting with a
reason so we're going to do string
memory reason equals cuz blue frog is
cool that's going to be my reason that's
why I'm going to vote for something
because blue frog is cool in order to
vote we're going to need to call this
cast vote function if we go to Governor
we look for the function Vote or excuse
me cast if we go on our search bar we
can look for function cast vote there's
a whole bunch of cast votes we can see
in here we need to pass a proposal ID
and
support how do we know support works
well let's look at this Casto internal
function this Caso internal function
down here support
count vote we got to look at this count
vote function so let's look for function
count vote okay this is the abstract
implementation let's we don't want
Governor compatibility Bravo we want
Governor counting simple because that's
we're using Okay support how does
support work aha okay so there's this
vote type struct that it's checking for
let's go to the vote type okay against
for abstain so against is going to be
zero
four is going to be one and abstain is
going to be two so what do I want to do
I want to write vote four so we're going
to do one so we're going to say we have
our reason un 8 vot way equals 1 which
means I'm voting for voting yes if you
will and now let's do our vm. prank our
user or our voter and we'll do
governor. cast vote
with reason proposal ID vote way and
reason reason all right cool now that we
voted let's speed up this voting period
because everyone voted because we're the
only voter let's speed up this voting
period and we set the voting period
automatically if we go back to our my
Governor right voting period was set to
one week in here this number represents
one week so we're we're going to speed
up this voting period I'm going to copy
this number scroll up to the top we'll
do U into 256 public constant uh excuse
me voting period equals this oh and this
should have a semicolon voting period
we're going to speed through the voting
period I'm going to just copy these
change this to
voting period voting period now now that
we' voted and the voting period is done
what do we do well we actually need to
cue the TX before we can actually for
execute so we have to cue it first so
there's a queuing process so on our my
Governor there's a function function q
and a whole bunch of them have this let
me open this up function
Q on this Governor timelock controller
there's this queuing bit because a que
means it's passed but we have to wait to
queue it up we just pass the same bits
that we originally did for propose so
we're going to go back here we'll say
the only difference is we need to Hash
the description first so we'll say bytes
32 D description hash equals check
256 api. in code pack description now we
can call governor. Q propal ID uh excuse
me this is not right
c.q um um targets we're going to pass
the same values that we proposed with so
that we guarantee it's the same proposal
so targets values
function uh call datas and description
hash oh we don't need that zero we're
going to queue it and then we're going
to
finally wait this m delay which we said
hey after a vote passes we need to wait
this m delay before we can execute so
I'm going to copy this again paste it
here instead of voting period this going
to be M delay M delay and then finally
we can execute so we'll do governor.
xute it's going to be the same as what
we did to queue it up I even just copy
this whole thing paste it here targets
values call Dat is description hash
X let me spell this right and now
finally we can do assert box dot get
number equals the value that we
stored
boom we even do like a console
diog box value box sucket
number okay copy this open our terminal
clear porch test DM paste - VV so we can
see the console. logs pull it up
is this what we've been waiting
for no we ran into an issue we see these
two logs proposal state is zero we're
getting vote currently not active so
let's add a couple more of these so we
can see where it failed it looks like it
got to the cast vote with reason and it
failed so let's go to our test so it
looks like it proposed okay it looks
like this failed so it says it's
currently not active so did we mess up
our voting delay ah it's cuz I did warp
for both this should be vm. roll let's
see do we do oh we messed that up for
all of them let's fix this roll roll
roll okay cool let clear let's run it
again hold this
up oh my goodness it passed and we can
see in the logs proposal state was Zero
we waited and then it became IM active
and and then finally after we got
through all the different states we
finally were able to update the box I
know we kind of sped through this
section but like I said at this point
you're becoming a pretty sophisticated
smart contract engineer and I really
wanted to show you the process of voting
of a typical Dow from today and for you
to see it from the coding side as well
now if you want to go further with this
I highly recommend you look into some of
the different methodologies and some of
the the newer methodologies that
researchers are coming out with for
people to actually do votes because like
I said plutocracy is not the best so if
you want to go farther try experimenting
with other ways to build Dows maybe
flesh out your own doubt maybe deploy a
doubt do whatever you want to do here
but in any case you should be incredibly
proud of yourself because you got
through the hardest lesson of this whole
course already which was the stable one
and then you just learned this
incredibly powerful Advanced course in
how Dows actually
work students we're almost at our end
there is one more lesson for us to do
and it's our smart contract and security
auditing for developers do not leave
this course without watching this I want
you to have at least the fundamental
knowledge of where you should look for
security help what you should have in
mind for security especially now that
you have the ability to go out and
deploy some amazing contracts I'm not
going to teach you how to audit step by
step here right but I am going to teach
you here's what you should be looking
for in an auditor here's how you you can
begin to start thinking about security
and I am working on some security and
auditing education material as well to
make those of you who do want to go down
that path successful so
give yourself a pat on the back do a
little dance do a little celebration get
some ice cream and I'll see you in the
next one and then I will I will release
you to the world so pause the video and
I'll see you
soon and as a special piece of bonus
content we have Harrison here to give us
some tips on gas optimizations so enjoy
hey guys I'm Harr Leo and I'm the CTO
and co-founder of pop punk LLC where
we're building Gaslight GG uh Gaslight
GG is a audit firm but we focus
specifically on gas optimization for
protocols to ensure that your gas costs
are as low as possible uh in addition
we're happy to announce that we're now
building uh hyper optimized public goods
tools for evm developers to make sure
that everyone has access to just the
best and the cheapest contracts so
Patrick invited me on here to show an
example of some some common ways that
you might be using too much gas in your
smart contracts Without Really noticing
so I have a quick example here we're
going to look at an airdrop contract
this is a super basic airdrop contract
which I'm calling airdrop bad or bad
airdrop uh very simple we have a token
we're using a counter to iterate um and
count the number of transfers uh so as
you see in the Constructor we set the
token here uh and then we have our
airdrop function so we have two
parameters um for this function the
first one is a memory um array of
addresses which is the recipients and
then we have a memory array of un 2506s
for the amounts the first thing we're
doing is we're checking to make sure
that the amounts match otherwise we're
going to revert the next thing here
which is really common um is making sure
that you're batching your your actions
so if we see in this case we have to
Traverse through the entire uh input
argument array a fulltime just so we can
transfer the number of tokens from the
message. sender to the contract so let's
say there's 50 amounts we'd have to do
50 separate transfer from it's
definitely not ideal uh and then lastly
here we have a super basic Loop where
we're just iterating through the array
um and we are transferring the tokens
from the contract to the user with the
respected amounts here uh lastly we can
see that we're iterating this counter
every single time in the array or every
time in the loop yeah so keep that in
mind so now if we look at a more ideal
example of this the first thing that
you're going to notice is we're using a
mutable for the token a mutable is good
if you're obviously never going to
change something because we set this
value in the Constructor and it'll be
included in the contract deployed by
code which is cheaper to read from every
time if we look now here we've changed
our arguments for our function the call
data and we've added a new one called
total amount this is done so we don't
have to do two Loops through the array
we can do one so if you see here the
first thing that we're doing is we're
transferring this total amount from the
message. sender to this contract so
that's going to cut a large amount of
gas because we're only going to wind up
looping one time so if we see here we
we've changed the way that we're going
through the loop technically we don't
have to Cache this array length because
we're using call data but if you do see
here it's the same thing we're
transferring tokens from this contract
to the recipient uh we're also using
unchecked for our Loop iterator because
it's not going to overflow cuz the odds
of someone doing an airdrop to more than
the max U 256 of addresses is kind of
crazy um and the final thing to look at
here we're using an unchecked block
again but rather than updating this
state value of transfers every single
time and paying that base G gas cost
each time we can just update this amount
at the end with the bulk length so these
are small things that you can think
about when you're writing your smart
contract to ensure that you're saving as
much gas as possible if we look at the
results of this the airdrop bad costs uh
1,
94,6 190 gas and the airdrop good cost
44842 gas so we've essentially saved
600,000 gas just by doing Simple things
you know it doesn't make the code
unreadable um and you're just making
sure that you know the end users of this
will not have to spend a crazy amount of
money so I want to thank Patrick for
having me on here uh you can find me on
Twitter atpunk onchain um or the
business account is atpunk LLC if you're
a small protocol or a large protocol
feel free to reach out for gas Audits
and uh pretty soon in the near future
you'll be able to go to gasl light.gg
and you'll be able to deploy super cheap
super gas optimized smart contracts you
know through no and just pressing one
button thank
you all right welcome back to our last
lesson of the course I hope you're
excited because I sure am in this last
lesson we're going to give you just a
teasing of smart contract security and
auditing this is especially for
developers we're not going to go over
everything security however in here I'm
going to leave a ton of places where you
can learn and grow and absolutely be
sure to sign up for web3 education.
because we're going to put a whole lot
more security information information in
here as well but we are going to give
you the foundational information that
you need to even begin to start thinking
about where to go and how to work with
security in the space we have this repo
Denver security to talk a little bit
more about security and how it works but
I want to give you all a couple of stats
before we even jump into security to
impress upon you why this is so
important this is probably the stat
that's the most jarring to me according
to chain analysis as of last year 2022
about $3.8 billion doll was stolen in
crypto hacks and about 3.1 billion of
that 3.8 billion was specifically Defi
and if we go to defi Lama last year defi
had a total value locked of around 50
billion so if we do the math there 3.1
ID 50 we're talking around 6% of all def
was hacked last year that's like walking
up to a bank and saying hey can I put my
money in you and the bank going yeah
sure just ah heads up there's a 6%
chance all your money will be gone next
year that is an insanely horrible
statistic and it's why we need to put so
much more focus and so much more
emphasis on security there's a popular
website called rec. newws that has a
leaderboard that if you go to you can
see a lot of the top biggest hacks
hugest hacks to ever existed did went
off of code that was unaudited
unreviewed by Security Professionals
unaudited unaudited unaudited uh Na Na
un audited unaudited unaudited unaudited
out of scope unaudited out of scope and
it is not acceptable some of these hacks
were absolutely massive losing over half
a billion dollars so if you're a
protocol or if you developers go on to
build protocols here is what you want to
think about you can maybe spend $2
million of your budget on security or
get hacked for $200 million the spending
money on security is a 99% reduction
cost so if you approach security from a
business perspective it absolutely 100%
makes sense for you to spend resources
on security so to kick start this video
all of you protocol devs will probably
want to get a smart contract security
audit at some point a security Focus
code review before you launch your
protocol so let's start there I've made
a video recently on what a smart
contract audit is and how to prepare for
a smart contract audit that we're going
to watch first because even if you don't
want to become a smart contract auditor
yourself you'll need to know what a
smart contract audit is for when your
protocol is going to go launch to
mainten it so let's start
there a smart contract audit is a
timeboxed Security based code review on
your smart contract system an Auditor's
goal is to find as many vulnerabilities
as possible and educate the protocol on
best security best practices and coding
best practices moving forward Auditors
use a combination of manual review and
automated tools to find these
vulnerabilities now why are these so
important why is it critical that you
get an audit before deploying your
codebase to a live blockchain well for
starters there are entire websites
dedicated to how many hacks happen last
year we saw the most value ever stolen
from Smart contracts with almost $4
billion stolen due to the immutability
of the blockchain once a smart contract
is deployed you can't change it so you
better get it right the blockchain is a
permissionless adversarial environment
and your protocol needs to be prepared
for malicious users but even more so
than that an audit can improve your
developers team's understanding of code
improving their speed and Effectiveness
in implementing features moving forward
and it can teach your team the latest
and greatest Tooling in the space often
just one smart contract audit isn't even
enough and protocols go on a security
Journey that includes many Audits and
many different services like formal
verification competitive Audits and Bug
Bounty programs we'll break these down
in a future video there are a lot of
companies that offer smart contract
auditing services like trail of bits
consensus diligence open Zeppelin Sigma
Prime spearit Dow mix bites watch pug
trust and of course cyphon additionally
there's a lot of independent Auditors
that do great work as well a typical
audit looks like this price and timeline
first a protocol needs to reach out and
they can reach out before or after their
code is actually finished ideally they
reach out sometime before their code is
finished so the audit can have time to
slot them in once they reach out the
protocol and Auditors will discuss how
long the auto will take based off of
scope and code complexity the scope of
the audit is going to be the exact files
and commit hash that's going to be
audited how long the audit usually
depends on how many lines of code SL
complexity you can see a very very rough
approximation of how long an audit takes
on your screen now of course this
depends firm to firm audit to audit and
tool to Tool so take these with a very
large grain of salt additionally it's
this duration that sets the price and
same thing at the time of recording
Prices range wildly depending on who's
doing the audit how many people people
are doing the audit how complex the code
is and more and these initial
conversations are really just to get a
ballpark estimate and Slot you into the
auditor schedule commit hash down
payment start date once you have a
commit hash you can finalize the start
date and final price the commit hash is
the unique ID of the codebase that
you're working with so the Auditors can
know exactly what code they're going to
be looking at some Auditors will ask for
a down payment in order to schedule you
in audit Begins the Auditors will use
every tool in their Arsenal to find as
many vulnerabilities in your code as
possible we'll give you some tricks in a
minute to make this a successful step
initial report after the time period
ends the Auditors will give you an
initial report that looks something like
this with all their findings listed by
severity usually categorized into highs
mediums lows informational noncritical
and gas efficiencies High mediums and
low represent the severity of impact and
likelihood of each vulnerability
informational gas and non-critical are
findings to improve the efficiency of
her code code structure readability and
best practice improvements that are not
necessarily vulnerabilities but more
ways to improve your code mitigation
Begins the protocols team will then have
an agreed upon time to fix the
vulnerabilities found in the initial
Auto report sometimes depending on the
severity of the findings this might mean
you have to start from scratch but more
times than not you can just implement
the recommendations the Auditors give
you this is usually much shorter than
the audit itself final report after the
protocol makes these changes the audit
team will do a final audit report
exclusively on the fixes made to address
the issues brought up in the initial
report then hopefully the protocol and
Auditors have a great experience
together and will work together in the
future to keep web 3 secure now there
are a few key things that you can do to
make sure your audit is successful as
possible to get the most out of your
audit you should have clear
documentation a robust test Suite
ideally including fuzz or invariant
tests code should be commented and
readable modern best practices are
followed there should be an established
Communication channel between developers
and Auditors during the audit and an
initial video walkthrough of the code
should be done before the audit starts
the most important part of the process
is going to be during the audit to get
the best results you want to think of
you and your auditor working together as
a team one of the best ways to do this
is to have a dedicated Channel where
Auditors can ask questions to the
developers the developers will always
and forever have more context over the
codebase than the Auditors ever will
because they have spent so much more
time working on the codebase and the
more documentation context and
information that you can give to the
Auditors the better this way it can be
easy for anybody to walk through the
code and understand what it's supposed
to do in fact 80% of all bugs are
actually business logic implementation
bugs this means that these are bugs that
have nothing to do with with some weird
coding error and are just somebody not
knowing what the protocol should be
doing so it's vitally important that the
Auditors understand what the code should
be doing having a modern test suite and
tooling can also make Auditors spend
less time fidgeting with your tooling
and more time finding issues post audit
we highly encourage you to take the
recommendations your Auditors give you
seriously additionally after an audit if
you make a change to your code base that
new code is now un audited code it
doesn't matter how small the changes
we've seen a ton of protocols saying oh
I'll just slip in one line of code and
sure enough that's the line of code that
gets exploited and often depending on
the seriousness of your protocol and how
many users you want to use it one audit
might not even be enough working with
multiple Auditors and getting more eyes
on your code will give you a better
chance of finding more vulnerabilities
what an audit isn't now here's the thing
an audit doesn't mean that your code is
bug free an audit is a security Journey
between the protocol and the auditor to
find as many bugs as possible and teach
the protocol different methodologies to
stay more secure in the future sec is a
continuous process that is always
evolving no matter how much experience
someone has people at all levels have
missed vulnerabilities on the
unfortunate day that that happens be
sure that you and your auditor can jump
on a call quickly to try to remedy the
situation and maybe consider getting
insurance for your protocol as well so
now with that being said now you have a
good idea of what a smart contract audit
entails and what to expect end to end a
smart contract audit is a security
Journey endtoend leveling up your
protocol so that you can have all the
best practices and security know how to
deploy your code to a live blockchain
forever and of course if you're looking
for an audit be sure to reach out to the
cyphon team Link in the description and
as always stay safe out there and we'll
see you next
time all right great welcome back so now
that you know a little bit more about
what a smart contract audit is let's
talk about the process that smart
contract Auditors and Security
Professionals take and the tools that
they use security is both for protocol
developers and for auditors you as a
smart contract developer need to know
all these tools and probably should use
all these tools before you even go to
audit at the end of the day it is you
who is responsible for the codebase that
you launch and additionally you often
can't just do security at the end you
have to have security built in from the
architecture from day one for example if
you build a car and the car is a total
piece of garbage and you say hey cool
it's time to race you're probably just
going to have to start over and and it's
not a very good use of time for you to
even go to the audit there are some
tools like security which which have
opinionated security and code quality
standard for solity smart contracts and
we showed you the simple security
toolkit from n XYZ for great places to
read before you go to audit and make
sure you have checked off or you've
looked at this doc to make sure you're
even ready to go to audit anyways let's
talk about the audit process so there is
no Silver Bullet but typically the smart
contract audit process goes through a
process of manual review and then using
tools and there are a ton of tools
Security Professionals use to make sure
code is secure now manual review is
probably one of the most important if
not the most important tool that we have
in our toolbox manual review is
literally going through the code line by
line and going through the docs and
making sure the code actually does what
it's supposed to do there's kind of a
joke in the developer Community where if
you just read the documentation for 15
minutes you'd be way better prepared to
actually do code but a lot of people
don't do that so step one on any audit
is really getting some understanding of
what the protocol should do most bugs
are actually business logic issues and
the only way you'll know if business
logic is wrong is if you understand what
the protocol or the business should do
and repetition is the mother's skill so
the more you read code the more you read
docs the more you do these audits the
better you'll get for example for
example if we had a contract like this
this caught with past we have this set
number function this code technically
isn't wrong right it'll compile fine
it'll deploy fine but maybe if we read
the docs and we read that set number was
supposed to set number to new number we
would then read this function and go oh
my gosh you're setting it to new number
plus one that's clearly wrong right and
the only way we would have caught that
bug is by understanding what the
function should be doing so that's
manual review now let's talk about some
of the other tools that we have in our
tool box here the first line of defense
is going to be test Suites which is why
we spent so long in this course writing
test Suites all the most popular
Frameworks out there have test Suites I
don't really need to go over them
because you have already done so static
analysis this is something we haven't
gone over static analysis is going to be
automatically checking code for issues
without executing anything hence the
debugging is static most of these tools
were sort of just dumbly look for
keywords in specific spefic orders and
AI is actually going to be an example of
static analysis as well and we'll show
you an example of all of these once I
get through this fuzz testing we've gone
over fuzz testing involves providing
random data as inputs during testing uh
we have stateful fuzz testing which
we've definitely gone over in this
course as well differential testing
which we aren't really going to go over
but it's a way to actually write the
same code multiple times and compare the
code against each other but then finally
there's formal verification now formal
verification is a generic term for
applying formal methods to verify the
correctness of Hardware or software
applying FM means anything based off of
mathematical proofs in software often
used as a proof of correctness or proof
of bug or just mathematically prove that
something in your code can happen this
can be a little bit confusing obviously
um but to me the way that I generalize
it is you basically take your solidity
function you take your solidity code and
you convert it to math and math can be
solved math has right and wrong answers
1 + 1 equals 2 right so we convert our
solidity code to math and then
mathematically prove or disprove it
symbolic execution is one of these
formal verification these formal methods
and symbolic execution involves
converting our code to a mathematical
expression there's some tools here like
Matt Manor Z3 zeror even the solidity
compiler actually has a symbolic
execution ciment there is a fantastic
article on hackam D by Paulina who goes
over some of these different symbolic
execution clients and Compares them to
each other I left a link to this of
course in the GitHub repo associated
with this course symbolic execution is
one of these tools that is the most time
intensive and a lot of protocols and a
lot of audit firms don't even go down
this route because even symbolic
execution isn't a silver bullet it's not
a guarantee that some code has no bugs
it's not a guarantee code is bug free
but especially if you have some
incredibly math computationally heavy
process symbolic execution or form of
verification might be a good tool to use
AI tools AI tools are a work in progress
long story short sometimes they're okay
and sometimes they're absolutely
terrible all right so now that we know
some of these tools let's actually see
them in action and I'm going to go kind
of quick here but what we could do is if
we're in the GitHub repo associated with
this course you can actually download
this Denver security re now this does
not have the- f23 at the end of it
because this is a lesson that I've done
in the past but it is associated with
this course what we'll do though is
we'll just clone it we're not going to
build a project from scratch here we're
just going to look at some code
ourselves so we'll do get clone paste it
in do code Denver security we'll open
that up in a new tab here and I and I
want to just show you how bugs can be
found with some of these different tools
here so in our SRC folder we've got a
couple of files here we have caught with
fuzz caught with manual review CAU with
slither staple fo symbolic CAU with test
Etc each of these has a bug in them
where one of these methods was going to
be how you catch it our first one CAU
with manual CAU with manual review we'll
do a toggle word wrap the way we can
catch this bug is just with manual
review right so we have this function do
math and the comments or the docs here
say Dev adds two to number two add and
returns it we can then easily read the
code and say oh my goodness we're only
adding one boom that's an issue that's a
bug that's something that we would want
to fix that's how we would catch this
one next CAU with test. soul this is the
one that we saw in the presentation that
we gave set number should set the number
a unit test would easily catch that this
was wrong all right what's next static
analysis or Slither now this is
something we haven't really gone over
but one of the most prevalent hacks in
this industry is going to be a something
called a re-entrancy attack and you can
read more about it in the solidity by
example re-entrancy I've left a link to
this of course in the get AO associated
with this course but they're actually
tools that will automatically check for
stuff like this if I pull up my terminal
and I use this tool slyther I've already
gotten installed you can go ahead and
check out the install instructions
yourself but if I run slyther Dot
it'll automatically compile my codebase
and then it'll give this massive print
out of a ton of the issues that it found
and right at the top right in red you
can see hey there's a withdraw issue
there's a re-entrance issue with your
withdraw function and then obviously we
could go back update it rerun Slither
and this would go away so a lot of these
issues have tools that you can just run
and it'll automatically detect so
slyther is one of these tools that I
think everyone should run on their
codebase before going to audit what's
next we've caught with fuzz which we
know what fuzz is we learned about
fuzzing we have this massive function we
see this here say should never return
zero so there's probably a fuzz test so
if we look in our fuzz test we actually
smartly wrote a test fuzz here where we
say hey the return number from that
function should never be zero we can
then grab that Forge test Forge test
d-mt what's it called test fuzz and we
see we get a failing test here we see we
found that if it passes 1
265 it actually will fail come back to
our test here and we can see there's a
conditional if my number equals 1265
1265 modular 1265 is going to be 0 + 1 -
1 * 1 is 1 which means it return zero we
don't want this to return return zero
which means we break our
invariant if we call change value with
zero it'll set my value to zero up here
and then we can call do more math again
with zero we're going to get 0id 1 + 0
return Z and boom this will break as
well and then finally we have symbolic
here where we can actually just use the
solidity compiler to do symbolic
execution we have these two functions
Function One Function One symbolic where
we have this revert in here and this is
going to be the bug in solidity we can
add asserts to our code base and we'll
go over a video that explains this more
in depth to tell our solidity compiler
hey this should not hit and this should
hit this conditional should not be hit
this one should hit in Foundry in our
Foundry doomo we can add information in
here to send to the solidity compiler
we're going to tell that our solidity
compiler to use the CHC model Checker
time out of 1000 and to look for those
assert keywords and if we just run Forge
build or Forge build-- fors to force it
to rebuild it'll compile and it'll give
us this output here saying warning CHC
assertion violation happens here meaning
it found an issue it found an input such
that this assert line hit so if we pull
up our terminal we can scroll down we
can see exactly we can see exactly what
the input is
8088 is the input that will allow that
to hit if we look 8088 / 4 does indeed
meet this criteria so it would go ahead
and revert here so those are some of the
tools we have in our toolbox we'll go
over symbolic execution a little bit
more but let's wrap this lesson
up so to talk to us more about the
process of manual reviewing we have
legendary ethereum smart contract
researcher tincho to walk us through his
process in a real process of him going
through ens he did an audit on ens Just
for kicks and ended up getting paid out
$100,000 for finding a critical
vulnerability so let's learn from
legendary manual reviewer tincho
this is Tino ethereum security
researcher previous lead auditor at open
Zepp and creator of damn vulnerable Defi
and today we have the pleasure of
talking with tincho going over his
auditing process so that you can learn
how to make damn un vulnerable
defi to do this we're going to be doing
a live mock audit of the ethereum name
service GitHub seeing exactly some of
the tools and techniques that tinel
would use to audit this I don't have
super formal auditing process I really
think that everybody will find their own
ways I will just show you briefly some
things that I do today link to the full
interview in the description let's get
froggy so this is a repository for inss
the first thing that I would do is
obviously go to repository you would
clone the repository to my local
environment but if you're like very
unfamiliar you should probably go to the
documentation at least I don't know read
the introduction now he says there's no
formal audit process but this sounds
like a good step one download the code
read the documentation read the
documentation here we have the
architecture is telling us like already
some keywords that we will need to
understand at some point such as what a
registry is what a resolver is already I
will get familiar with this probably
these are contracts that I'm about to
see in the code and so on and so forth
one thing that you can do also after
reading some documentation is looking at
audit reports if we go to the actual
code we will realize that there are lots
of things wait a minute what the heck is
that look logo what wonky text editor is
tincho using that my friends would be
vscodium it's different from vs code vs
code is a product owned by Microsoft
that actually sends a lot of your usage
information over to Microsoft vscodium
doesn't do this they have removed I
think Telemetry and some related to
Microsoft tjo said he's just been trying
it out recently but maybe it's a
security Alpha leak so multiple
contracts in here already we see that
lots of folders it's using harad from
what I can tell these days I like that
use Foundry more than those that use
herut so in that case what I would do is
I created another folder in which I have
a Foundry local setup why do you like
Foundry better why do you make this
Foundry local setup it's faster and I
can write quick test only using solidity
I will do whatever thing that I want to
do here but just an easy way to have
something quick and dirty to test things
quickly bring and use the tools that
you're most familiar and best with I
think that's super important and don't
be afraid to bring your disgustingly
horrible dirty dirty
tests but anyway already we saw that
it's quite complex so what I would do in
this case is there is a command L
utility that I would use which is called
celoc celo will help you count lines of
code and so I would use celo it would
give me a nice output that you can
actually pass to a CSV and instead of
doing this here what I would usually do
is I would move that to a spreadsheet I
have the scope for in right all these
files are now ordered here and now I can
have a view in terms of how many files
do I have how complex they might be so
apparently I have 59 files and now I
know the name wrapper will be one of the
most complex conts perhaps right because
it has more than 700 lines of code
another approach to do this scoping
phase you can actually use this tool by
consensus which is called solidity
metric so you can run it on a project
and it will actually give you a nice
report of the code ways and level of
complexity and then I have a column
stating where the thing that I do is not
started it's in progress or it's done so
this is his next step he either uses
solidity metrics or CL loock ranks out
the contracts that he needs to audit
based on complexity and starts going
through it moving contracts from not
started to in progress to done a very
organized approach when you is alone in
might seem silly but when you work in
teams it's quite important as the a
progresses I will be less and less
focused on this file because probably
this is super complex and will related
to either but most of all it's very
useful at least for me at the beginning
of the just understand what am I looking
at once I have this table I usually
start with the little Legos and then I
go move up in complex so in this case I
will probably I don't know start with
the erc20 recoverable contract here it
is and it's quite short say okay it's
ownable inheriting from open saing as
aitor probably I can take that for
granted which is out of the scope and I
will assume that's working correctly and
it has a single function to recover fun
okay it has access control so this is
probably fine as long as they are
handling access controls in the right
way this is fine and it's actually doing
this right so we start with the small
little building blocks or Legos as
tincho said and now you're going to see
Tino's brain start switching into how
can I break this as an aor you might
start wondering where this is good for
any token out there right where it's
possible to actually execute a transfer
on any address that the owner passes
here and where that could be problematic
for behaved erc20 tokens and if you're
familiar with usdt for example that
could be problematic in this ah now
we're seeing him drawing on his
expertise knowing that usdt is a weird
token usdt actually doesn't return a
Boolean on its transfer froms whereas a
lot of other tokens actually do what
usdt when you see that only owner
function do you think okay is this a dow
is there a single person who controls
what are your thoughts at least at the
beginning I wouldn't worry about it too
much at some point I will read
documentation about roles of this but
yeah at some point I should probably
understand at some point Who's the
actual owner okay say that you think
that this is okay so what I would do
usually is I would take notes in the
code right so in this case I would say
like Access Control okay for example
just to have a note saying that I was
here or you can have a question like
Patrick said Is this governance right
let's say this was an issue so I would
do this shouldn't be I know let's say
shouldn't be owner another thing that I
do to take notes is actually have noes
files in the same place very R nodes
very having a file where I can quickly
dump ideas that I have some point things
so go well I would have an issues list
here I will start listing I know in line
blah blah blah of file blah blah blah
issues take notes in the code in a notes
file somewhere just have a place where
you can dump thoughts maybe you can even
use a note taking plugin I don't use any
plugins that's because I don't like
having the UI clut too much with stuff
yeah no taking extension was a dumb idea
and one thing that you have to be
careful when you start with the very
little things so it's very easy to go
deep into the rabbit hole of the single
thing that you are trying to AIT is as
for example I have very little knowledge
of DNS I'm not like super familiar with
the and I got like very like down into
DNS because I was just paying attention
to this single resolver and that led me
to realize that after I know two days I
was growing familiar with DNS but I was
losing the big picture of actually hey
I'm actually putting ens so it's very
easy to go deep into the rabbit hole of
the single thing that you trying to
audit remember to jump out of the rabbit
holes perfect so at some points in
during the audit you will realize that
you might need to test things so in this
case I saw these two functions one is
going from my bites thing she returning
an address and the other is doing the
other way around it you might say it's
okay but if you're lazy you can actually
use fun to help you in that so what I
did in this case I used my very hungry
hacky boundary repo that I have in here
and I actually copied these two
functions to a contract like I just took
them not even trying to do fancy Imports
or whatever just BR copying and pasting
them here and I did a quick test uh F
test in which I provide an address I
pass that address to this for function
and the result of that I pass it to the
other function and I want to make sure
that I always get the same address as I
said a lot of times yes you're going to
write code and not just do manual
reviews T here wrote what's called a
fuzz test which you should absolutely
smash that like button because we're
going to have a video on that sometime
in the future use your tools to validate
findings that you have an inkling are
wrong in the cases where I have to set
up more complex stuff perhaps having a
separate folder with a single project is
not that convenient because I would need
to set up the whole in system and that
wouldn't be that convenient so in those
cases I would go to the actual testing
environment of the project if you're
doing a private out how important is the
process of of talking and interacting
and and keeping Communications with the
client open I would say it's almost
fundamental usually developers will have
much more context than you as an auditor
on what the system is intending to do so
you can spend a whole week trying to
figure out on your own where this
modifier should be in this function or
not but if you actually send a question
to the client and tell them hey should
this be here or not and they will tell
yes it should be here you can see it in
test blah blah blah you can see them as
companions during the O and you should
rely on them you can rely on the client
AO protocol is your code good you think
so hell yeah my work is done having said
that it's also important not to trust
too much at the end of the day they are
trusting you as the expert so in this in
that sense I would advise okay keep the
clients at hand ask questions but also
be detached enough since they built the
code they have spent more time time
thinking about the code and looking at
the code than you ever will ask them
questions and don't be afraid to ask
questions I guess at this point what's
the next step are we wrapping up now are
we writing the report up what do we do
next the thing is I always get the
feeling that you can be looking at a
system forever it there's always one
additional line that you can check
there's always one additional attack
that you can think of one additional
potential C my vulnerability and
everything so what I do is I time bound
myself to have a certain level of
confidence when you're shipping the
report that you did your best and you
thought of every single possible attack
or vulnerability that you could think of
in that limited amount of time time
bound
yourself when you're going through this
code how are you thinking of different
attacks like when you're looking at a
piece of code how do you get that
context of what different types of
attacks to think of yeah I don't have a
checklist very difficult to translate
experience in doing it have this
adversarial mindset of try to at least
there lots of knowledge that can come
from all every single day reading
vulnerability reports every single
daying or reading responsible
disclosures that are published reading
all this reports I read newsletters like
I don't know I have this constant influx
of in security related information to
solidity that little by little I think
you start growing the intuitions the
experience that actually help you
identify quickly things that can happen
in smart contracts and always remember
that you can miss things there's no per
auditor I think that everybody has
audited sufficient enough and complex
enough systems they have all mied issues
and it's okay security is a is a thing
that we have to approach from many
different angles and auditing is just
one thing that must be done but is not
the only one knowing that you're doing
your best in that knowing that you're
putting your best effort every day
growing your skills learning grow and
intuition and experienceing you
something that I always say is to audit
to me is 50% finding vulnerability and
50% delivering readable report once the
client starts fixing the issues they
will send you the fixes for the issues
and what you have to do at that point is
actually review the fixes and make sure
that not only the vulnerability that you
highlighted in the report is fixed but
actually this have been introduced by
the fix and then you wrap up your whole
auditing process with writing a very
good report and take the time to do so
once you give them the report they will
go ahead and fix the issues come back
say hey we fixed them and then it's your
job to make sure that they fix the
issues and they didn't reintroduce new
bugs let's say you give your audit
report done your time box you've done as
much as you can you think you did a good
job four months goes by oh my God 100
million dollar hack they've ended it up
on wrecked what do you do what happens
let me approach you slowly okay all we
first say I have always been of the idea
a security code review should be
valuable enough beyond the fact that I
find or not find a critical issue so I
should be able to provide value to
whoever is working with me to whoever is
trusting me beyond the fact that I did
or did not find a crial isue obviously
the less critical issues that you miss
the better the safer and perhaps they
miss something and that can happen and
has happened and will continue to happen
but it's naive to think in my opinion
that just because an auditor miss
something whole blame of thing is on the
audit this I think is a really important
final thought you as an auditor it is
not solely your job to make sure their
code is bug free you share that
responsibility with the client however
this doesn't give you free range to suck
at your job people will notice tcho
thank you so much for being here thank
you for all you've done for the web3
community and I'm sure everyone will get
a lot from this okay padrick thank you
for inviting me bye bye
[Music]
so to understand symbolic execution a
little bit more we've got some members
from the trail of bits team to teach us
about symbolic execution and formal
verification formal verification is the
act of proving or disproving a given
property of a system using a
mathematical model symbolic execution is
is one technique used for formal
verification symbolic execution explores
different paths in a program creating a
mathematical representation for each
path or more plainly converting your
code to a set of mathematical
Expressions is this the Silver Bullet
for your auditing Journey let's get
froggy I had the absolute pleasure of
interviewing trailer bits head of
engineering Joselyn and security
engineer from Trailer bits as well Troy
about fuzzing testing methodologies and
importantly formal verification I have
links to the full interviews with both
of them in the description and in this
video we're going to summarize a lot of
that and focus on what formal
verification and symbolic execution
actually are but to do that we need a
quick refresher on some of the testing
methodologies that we use in web 3 if
you haven't seen my invariant testing
video be sure to watch that first before
coming here and to really understand
this you do need at least a high level
understanding of solidity as well what
tools do we have in our toolbox to have
high Assurance about our program layer
one the unit test obviously you have
unit test which you do a very specific
thing like this function does this and
you start looking at things like test
code coverage like statement and Branch
coverage and I think that's kind of the
bare minimum the absolute bare minimum
in security is a unit test for example
if this is our solidity and we have a
set number function which should set our
number variable to whatever new number
is a unit test would be able to catch
this and if we're using Foundry we could
get an output that looks like this so
I'm assertion failed and we can go see
our test and see that when we call set
number toine number we assert that we
get what we want and if that breaks cool
our unit tests have saved us Foundry
hard hat apworks truffle brownie and all
the most popular Frameworks have unit
test built in Layer Two the fuzz test
fuzzing is where you take random inputs
and run them through your program as you
have to Define things in your code that
you always want to hold true and fuzz
testing is the new bare minimum for web
3 security because I decree and as Troy
said you need to understand the property
or invariant of your system to do
fuzzing once you have your property
defined you throw random data at your
system in order to break that property
if you find something that breaks it you
know you have an edge case that you need
to refactor your code to handle for
example if I have a function like do
more math and I know that this function
should never return zero I can throw a
whole bunch of random numbers in here to
try to get it to return zero I can have
a fuzz test that looks like this which
passes in a whole bunch of random
numbers and tries to assert that we
never get zero and we can see from our
fuzz test it was able to pick a random
number in order to break our invariant
or property now like I said we recently
did a video on fuzz testing or invariant
testing and I highly recommend everybody
watch that video because I de create
Foundry AK kidna and consensus diligence
fuzzer are some popular fuzz testing
tools okay what's next layer three
static analysis so the things that we've
been discussing unit testing and fuzz
testing or dynamic testing which Dynamic
just means like you're you're actually
doing something Dynamic analysis is when
we run or we execute our code in unit
and fuzz tests we do exactly this we are
running our code to try to break it in
static analysis we just look at our code
or have some tool look at our code for
example this code here has a classic
re-entrancy vulnerability in our
withdrawal function if we run a static
analysis tool like slyther it'll
automatically detect hey you have a
re-entrancy vulnerability and this is
great for very quickly picking out very
specific parts of your code that are
known to be bad practice some popular
tools for static analysis are going to
be Slither created by the trail bits
team and even the solidity compiler
could be considered a static analysis
tool so now that we have a little bit of
a backstory on some of the popular
layers of testing and keeping ourselves
secure let's jump into formal
verification and symbolic execution now
layer four formal verification so on a
high level formal verification is going
to be the act of proving or disproving a
given property of the system this is
usually done through a mathematical
model of the system and the property
there's that word again property you're
seeing that almost no matter what you're
doing in your testing you need to
understand the properties of your system
and right there JN gave us some of the
keys between fuzz testing and formal
verification fuzz testing tries to break
Properties by throwing random data at
your system whereas formal verification
tries to break properties using
mathematical proofs and there are many
different ways to do formal verification
such as symbolic execution and Abstract
interpretation for this video we're
going to focus on symbolic execution as
that's one of the most popular ways
currently done in web 3 symbolic
execution is then one of the techniques
that you can use to do formal
verification symbolic execution on a
high level is going to be a technique
where you are going to have your program
and you're going to try to explore the
different path of the program for every
execution paths you're going to create a
mathematical representation additionally
if you want to learn more about symbolic
execution outside of web 3 I left a link
in the description to an MIT open
courseware video which does a great
rundown of symbolic execution now let's
look at Jin's example of using formal
verification and symbolic execution
let's say this is our function that we
want to do formal verification on and
we're going to do symbolic execution to
do form of verification well let's go
back to what formal verification
actually is formal verification is the
act of proving or disproving a given
property of a system using a
mathematical model so the first thing we
need to do is figure out what we want to
prove or disprove for our demo we should
say our invariant this should never
revert and that's what we're going to
try to prove or disprove now this might
seem like a silly example but you can
imagine that this was a function called
withdraw money and you want users to
always be able to get their money out
which would seem like a much less silly
example symbolic execution is going to
be creating a mathematical formula for
this function f from our code we're
going to convert this function to a
mathematical logical representation of
every execution path from our code once
we have a set of math functions we can
push those into a solver which will tell
us if a property is true or false or if
our invariant is true or false when we
talk about the different Paths of our
function we can imagine okay one path
that our function can take is going to
be a + 1 being returned that's going to
be one of our paths but we can also
imagine that a + 1 is actually going to
overflow and therefore revert if we pass
in the maximum U 256 A+ 1 would revert
so that would be a second path that our
function could take and a symbolic
execution tool would find this second
path for us programmatically and then of
course convert these to a set of
mathematical Expressions this set of
mathematical Expressions might look
something like this a and also not a
imagine if you ask somebody can this set
of mathematical Expressions be true at
the same time the answer would obviously
be no it is impossible for both a to be
true and a to be false this is what our
solver is going to figure out for us but
with much more complicated Expressions
than this in our example the solver is
known as a SAT solver or an smt solver
and there many different types of
solvers but I'm not going to go too deep
into that right now running our symbolic
execution tool we would see two paths
our first path going to be if we give it
the maximum size of a un 256 we try TR
add one to it the function will revert
solidity doesn't allow you to add one to
the maximum size of a un 256 the other
path is anything lower than the max size
of un 256 we'll just add plus one and
then return so those are going to be our
two paths path one a is not 2 ra 256 - 1
and then a returns normally and a is 2
ra 256 and it reverts however instead of
a b c not a etc our symbolic execution
tool will give us an output that might
look something like this now what you're
seeing on screen is an smt lib language
and it's a language specifically made
for working with these solvers to solve
our mathematical representations of our
code I'm not going to go over too deeply
what this is doing but you can consider
this just a list of Boolean Expressions
kind of like a and not a and if a
therefore B and A B and C Etc now if you
take this code and paste it into a tool
like Z3 or run it locally on your
machine it'll give you an output that
looks something like this sat and sat
this first that is saying they were able
to find an input for path 1 and they
were able to find an input for path two
they were able to satisfy the booleans
in those different paths since it was
able to satisfy an input for path two
and we know that path 2 reverts we know
that this invariant is now broken
because our invariant is it must never
revert and our mathematical
representation said hey I've
mathematically proven that there is a
scenario where path 2 is executed and
your function reverts so sat here means
we mathematically proved that this
invariant breaks now I manually created
this smt lib list with the help of chat
GPT however symbolic execution tools
like mantacore hvm and even the solidity
smt Checker can give you this smt output
but all those tools come with a Z3 buil
in so they'll even just skip this step
and just give you hey is my invariant
broken or not even the solidity compiler
itself can do this entire process behind
the me explore the paths convert the
paths to a set of Boolean and check to
see if those paths are reachable or not
using the so compiler we can run with
model Checker engine and we can look for
an overflow in small soul. soul and if
we run this you'll see the solidity
compiler was able to do symbolic
execution to find out hey if I add the
maximum unit 256 as an input to here
you're going to get an overflow and that
function is going to revert now
obviously reverts are pretty easy to
find but we can even add assert a does
not equal 1 rerun this but instead of
overflow look for asserts and we would
see that again it was able to
mathematically find an input to break
our assert or our invariant said if you
add zero you're going to get one asserts
are what you're going to use if you want
to use more complicated and more
specific symbolic execution rather than
just overflow or underflows tool like
mantor will also give you an output with
the revert as well as a much more hypers
specific list of S&T Li that it's
inputting into its Z3 solver so a lot of
stuff just happened here let's recap we
built some solidity we understood our
invariant and the next two steps
happened at the same time with sulk or
manacore we used a symbolic execution
tool like the built-in one to solidity
to create a set of bluing Expressions
that represent every execution path of
our code and then we dumped them into a
solver like Z3 to see if our property
could be broken just by running this one
function solidity was able to do all
those steps behind the scenes for us we
go through a full walkthrough of this
example with the interview with JN so be
sure to check that out as well if you
want to learn more and don't be afraid
if this seems a little bit complicated
be sure to ask questions leave comments
in the descriptions and leave a like as
well and hopefully I'm going to clear
this up for you sometimes the solver
might not be able to solve the equation
too like if the equation is too complex
you usually provide a time oute to the
server just because you know if you have
to invert hash function you know good
luck to do that with the server well so
you're saying this isn't a silver bullet
for auditing like any technology even
formal verification and symbolic
execution abstract interpretation the
these are not a one-sized fits-all
approach using symbolic execution can
run into something called The Path
explosion problem where there's too many
paths for a computer to explore in a
reasonable amount of time and a solver
would never be able to finish how
practical is it to to take all these
steps how hard is this to really do well
there are a couple of things to consider
the first one is that this specific
technique symbolic execution as a couple
of limitation as you're going to explore
the different path of the program there
is a problem which is called the path
explosion problem which is basically if
you have too many paths to explore in
the program it's going to take forever
if you have an infinite Loop this
technique require significant effort to
be used you need to understand how they
work and you need to understand their
limitation and how to help them and also
significant effort to be maintained at
the end of the day I think what really
matters are the properties if you want
to know if a bug can occur and if the
property can be broken you don't
necessarily need formal method for that
and you can use a further which is way
easier to use and provide like kind of
the same type of value something that is
sometimes difficult for people is really
to understand how to create this
property and how to create enance for
themselves if someone wants to you know
learn a bit more we have this website
it's called secur track.com where we go
a lot of guidance and best practices and
among other we have tutorial on how to
use Aina how to define inv variant how
to think about properties there's a high
skill requirement to using these tools
itively at the moment people are working
on making them easier and easier like
the built-in solidity smt Checker
however as Jon's been saying sometimes a
sufficiently powerful fuzzer is all you
need and if you combine a fuzzer with a
symbolic execution back end the fuzzer
can pick better random numbers based off
of the symbolic execution and maybe you
can find all the answers you need
without having to do a formal
verification test suite and the most
important piece here is that even this
isn't a guarantee your code is bug free
all it does is mathematically prove your
code does that one specific thing
correctly I'm hoping as AI takes off
doing a lot of this will become much
easier and I guess we'll have to see but
for now hopefully you learned at least
the basics of symbolic execution if
you'd like to learn more leave a comment
in the description if you want to see
more videos about form of verification
let me know but the takeaway from this
is that you should become a stateful
fuzzing wizard thanks all for watching
be absolutely sure to go watch the
interview with Troy and Joselyn links in
the description and we'll see you next
time
[Music]
now for those of you who are looking to
become Security Professionals smart
contract security Wizards there are a
ton of resources you should actually
check out I've left a link of course in
the GitHub repo associated with this
course to this comprehensive list of
known attack vectors and common anti
patterns it's got a ton of different
solidity attack vectors that you should
100% know about additionally if you want
to learn more on doing security there's
a phenomenal game by a developer named
tincho called damn vulnerable defi which
is a way to learn offensive security of
defi smart contracts in ethereum and
they've got these amazing games they you
can check out and play to learn security
ethernut is another game that is
absolutely phenomenal that will help you
to learn and understand solidity and
smart contract vulnerabilities for those
of you who are looking to become
Auditors definitely be sure to check out
solid it which is this amazing tool for
you to check out audit reports from the
top smart contract security Prof
Professionals in the entire industry of
course be sure to check us out at cyphon
we do smart contract security and
auditing as well if you are building a
protocol yourself definitely be sure to
check us out for your next project for
your next protocol for any and all of
you absolutely subscribe to web3
education. deev we have some amazing
surprises coming for you in the next
couple of months and I don't want you to
miss it there's going to be some awesome
opportunities for you to participate
learn and grow in top security so be
sure to put your email in here and this
is really just the tip of the iceberg
there's so much more to go when it comes
to security we are working on more
Security based education as well so stay
tuned for that but the prerequisite for
the security course is this monster
Foundry course anyway so you have
already taken the first step to becoming
a smart contract security badass as well
but all right guess
what that's it for the security lesson
congratulations I and the web3 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 finished 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 web3
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 3 in the blockchain community
now a lot of people ask well where do I
go now I didn't have all this new found
knowledge I'm armed with the
intelligence of the web 3 developer
space well I've left some links in the
GitHub repository to lend 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
boot camp 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 onto 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
handholding you is where you're going to
learn 10 times as much is what you've
learned here I've walked you 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 so
excited to see you in the community and
see what you build and see what we can
create with this technology
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.